1、背景
線上你寫代碼的時候,想固然的認爲寫進 redis 的數據就必定會存在,後面致使系統各類 bug,誰來負責?
常見的有兩個問題:
往 redis 寫入的數據怎麼沒了?
可能有同窗會遇到,在生產環境的 redis 常常會丟掉一些數據,寫進去了,過一下子可能就沒了。個人天,同窗,你問這個問題就說明 redis 你就沒用對啊。redis 是緩存,你給當存儲了是吧?
啥叫緩存?用內存當緩存。內存是無限的嗎,內存是很寶貴並且是有限的,磁盤是廉價並且是大量的。可能一臺機器就幾十個 G 的內存,可是能夠有幾個 T 的硬盤空間。redis 主要是基於內存來進行高性能、高併發的讀寫操做的。
那既然內存是有限的,好比 redis 就只能用 10G,你要是往裏面寫了 20G 的數據,會咋辦?固然會幹掉 10G 的數據,而後就保留 10G 的數據了。那幹掉哪些數據?保留哪些數據?固然是幹掉不經常使用的數據,保留經常使用的數據了。
數據明明過時了,怎麼還佔用着內存?
這是由 redis 的過時策略來決定。
2、分析
redis 過時策略
redis是單線程,收割時間也會佔用線程處理時間,若是收割過於頻繁,會致使讀寫出現卡頓。
一、主庫過時策略
1.一、定時掃描
首先將每一個設置了過時時間的key放到一個獨立的hash中,默認每秒定時遍歷這個hash而不是整個空間:
並不會遍歷全部的key,採用一種簡單的貪心策略
1.1.一、從過時key字典中,隨機找20個key。
1.1.二、刪除20gekey中過時的key
1.1.三、若是2中過時的key超過1/4,則重複第一步
1.1.四、每次處理的時間都不會25ms
若是有大量的key在同一時間段內過時,就會形成數據庫的集中訪問,就是緩存雪崩!
1.二、惰性策略
客戶端訪問的時候,會對這個key的過時時間進行檢查,若是過時了就當即刪除。惰性策略是對定時策略的補充,由於定時策略不會刪除全部過時的key
二、從庫過時策略
redis不會掃描從庫,刪除主庫數據的時候,在aof文件裏生成一條del指令,在主從同步的時候,從庫會執行這條指令,刪除過時key。
因此集羣分佈式鎖算法的漏洞就是這樣產生的。
3、常見的幾種緩存失效策略
FIFO ,first in first out ,最早進入緩存的數據在緩存空間不夠狀況下(超出最大元素限制時)會被首先清理出去
LFU , Less Frequently Used ,一直以來最少被使用的元素會被被清理掉。這就要求緩存的元素有一個hit 屬性,在緩存空間不夠得狀況下,hit 值最小的將會被清出緩存。
LRU ,Least Recently Used ,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又須要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。
4、緩存更新策略
更新緩存的設計模式有四種:Cache aside, Read through, Write through, Write behind caching
一、Cache aside
讀取:
失效:應用程序先從cache取數據,沒有獲得,則從數據庫中取數據,成功後,放到緩存中。
命中:應用程序從cache中取數據,取到後返回。
更新:先把數據存到數據庫中,成功後,再讓緩存失效。
Read/Write Through Pattern
咱們能夠看到,在上面的Cache Aside套路中,咱們的應用代碼須要維護兩個數據存儲,一個是緩存(Cache),一個是數據庫(Repository)。因此,應用程序比較囉嗦。而Read/Write Through套路是把更新數據庫(Repository)的操做由緩存本身代理了,因此,對於應用層來講,就簡單不少了。能夠理解爲,應用認爲後端就是一個單一的存儲,而存儲本身維護本身的Cache。
二、Read Through
Read Through 套路就是在查詢操做中更新緩存,也就是說,當緩存失效的時候(過時或LRU換出),Cache Aside是由調用方負責把數據加載入緩存,而Read Through則用緩存服務本身來加載,從而對應用方是透明的。
三、Write Through
Write Through 套路和Read Through相仿,不過是在更新數據時發生。當有數據更新的時候,若是沒有命中緩存,直接更新數據庫,而後返回。若是命中了緩存,則更新緩存,而後再由Cache本身更新數據庫(這是一個同步操做)
四、Write Behind Caching Pattern
Write Back套路,一句說就是,在更新數據的時候,只更新緩存,不更新數據庫,而咱們的緩存會異步地批量更新數據庫。這個設計的好處就是讓數據的I/O操做飛快無比(由於直接操做內存嘛 ),由於異步,write backg還能夠合併對同一個數據的屢次操做,因此性能的提升是至關可觀的。
可是,其帶來的問題是,數據不是強一致性的。
redis 過時策略是:按期刪除+惰性刪除。
所謂按期刪除,指的是 redis 默認是每隔 100ms 就隨機抽取一些設置了過時時間的 key,檢查其是否過時,若是過時就刪除。
假設 redis 裏放了 10w 個 key,都設置了過時時間,你每隔幾百毫秒,就檢查 10w 個 key,那 redis 基本上就死了,cpu 負載會很高的,消耗在你的檢查過時 key 上了。注意,這裏可不是每隔 100ms 就遍歷全部的設置過時時間的 key,那樣就是一場性能上的災難。實際上 redis 是每隔 100ms 隨機抽取一些 key 來檢查和刪除的。
可是問題是,按期刪除可能會致使不少過時 key 到了時間並無被刪除掉,那咋整呢?因此就是惰性刪除了。這就是說,在你獲取某個 key 的時候,redis 會檢查一下 ,這個 key 若是設置了過時時間那麼是否過時了?若是過時了此時就會刪除,不會給你返回任何東西。
獲取 key 的時候,若是此時 key 已通過期,就刪除,不會返回任何東西。
可是實際上這仍是有問題的,若是按期刪除漏掉了不少過時 key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?若是大量過時 key 堆積在內存裏,致使 redis 內存塊耗盡了,咋整?
答案是:走內存淘汰機制。
內存淘汰機制
redis 內存淘汰機制有如下幾個:
- noeviction: 不刪除策略, 達到最大內存限制時, 若是須要更多內存, 直接返回錯誤信息。 大多數寫命令都會致使佔用更多的內存(有極少數會例外, 如 DEL )。
- allkeys-lru:全部key通用; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
- allkeys-random: 全部key通用; 隨機刪除一部分 key。
- volatile-lru:只限於設置了 expire 的部分; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
- volatile-random:只限於設置了 expire 的部分; 隨機刪除一部分 key。
- volatile-ttl:只限於設置了 expire 的部分; 優先刪除剩餘時間(time to live,TTL) 短的key。