redis(十二:淘汰機制)

MySQL 中有 1TB 的數據,如果我們使用 Redis 把這 1TB 的數據都緩存起來,雖然應用都能在內存中訪問數據了,但是,這樣配置并不合理,因為性價比很低。
一方面,1TB 內存的價格大約是 3.5 萬元,而 1TB 磁盤的價格大約是 1000 元。另一方面,數據訪問都是有局部性的,也就是我們通常所說的“八二原理”,80% 的請求實際只訪問了 20% 的數據。所以,用 1TB 的內存做緩存,并沒有必要。


在實踐過程中,我看到過的緩存容量占總數據量的比例,從 5% 到 40% 的都有。這個容量規劃不能一概而論,是需要結合應用數據實際訪問特征和成本開銷來綜合考慮的。建議把緩存容量設置為總數據量的 15% 到 30%,兼顧訪問性能和內存空間開銷。

Redis 緩存有哪些淘汰策略

eviction
默認情況下,Redis 在使用的內存空間超過 maxmemory 值時,并不會淘汰數據,也就是設定的 noeviction 策略。對應到 Redis 緩存,也就是指,一旦緩存被寫滿了,再有寫請求來時,Redis 不再提供服務,而是直接返回錯誤。

過期時間相關的淘汰機制
我們使用 EXPIRE 命令對一批鍵值對設置了過期時間后:1,這些鍵值對的過期時間是快到了;2,Redis 的內存使用量達到了 maxmemory 閾值。(volatile-開頭的策略 ,如果一個key還未過期,還是可能被刪的)
Redis 都會進一步按照 volatile-ttl、volatile-random、volatile-lru、volatile-lfu 這四種策略的具體篩選規則進行淘汰。
1,volatile-ttl 在篩選時,會針對設置了過期時間的鍵值對,根據過期時間的先后進行刪除,越早過期的越先被刪除。
2,volatile-random 就像它的名稱一樣,在設置了過期時間的鍵值對中,進行隨機刪除。
3,volatile-lru 會使用 LRU 算法篩選設置了過期時間的鍵值對。
4,volatile-lfu 會使用 LFU 算法選擇設置了過期時間的鍵值對。

allkeys淘汰機制-無視過期時間
allkeys-lru、allkeys-random、allkeys-lfu 這三種淘汰策略的備選淘汰數據范圍,就擴大到了所有鍵值對無論這些鍵值對是否設置了過期時間。它們篩選數據進行淘汰的規則是:
1,allkeys-random 策略,從所有鍵值對中隨機選擇并刪除數據。
2,allkeys-lru 策略,使用 LRU 算法在所有數據中進行篩選。
3,allkeys-lfu 策略,使用 LFU 算法在所有數據中進行篩選。

以上的淘汰機制都是定時刪除沒掃描到的老賴,當內存不足時,進行淘汰。

LRU (Least Recently Used)算法
volatile-lru 和 allkeys-lru 策略都用到的 LRU 算法。LRU 算法按照最近最少使用的原則來篩選數據,最不常用的數據會被篩選出來,而最近頻繁使用的數據會留在緩存中。
LRU 會把所有的數據組織成一個鏈表,鏈表的頭和尾分別表示 MRU 端和 LRU 端,分別代表最近最常使用的數據和最近最不常用的數據。


LRU 算法背后的想法非常樸素:它認為剛剛被訪問的數據,肯定還會被再次訪問,所以就把它放在 MRU 端;長久不訪問的數據,肯定就不會再被訪問了,所以就讓它逐漸后移到 LRU 端,在緩存滿時,就優先刪除它。
不過,LRU 算法在實際實現時,需要用鏈表管理所有的緩存數據,這會帶來額外的空間開銷。而且,當有數據被訪問時,需要在鏈表上把該數據移動到 MRU 端,如果有大量數據被訪問,就會帶來很多鏈表移動操作,會很耗時,進而會降低 Redis 緩存性能。

Redis 中簡化的LRU 算法
在 Redis 中,LRU 算法被做了簡化,以減輕數據淘汰對緩存性能的影響。
具體來說,Redis 默認會記錄每個數據的最近一次訪問的時間戳(由鍵值對數據結構 RedisObject 中的 lru 字段記錄)。然后,Redis 在決定淘汰的數據時,第一次會隨機選出 N 個數據,把它們作為一個候選集合。接下來,Redis 會比較這 N 個數據的 lru 字段,把 lru 字段值最小的數據從緩存中淘汰出去。(抽樣-》比較-》淘汰)
當需要再次淘汰數據時,Redis 需要挑選數據進入第一次淘汰時創建的候選集合。這兒的挑選標準是:能進入候選集合的數據的 lru 字段值必須小于候選集合中最小的 lru 值。
lru也是有局限性的,比如一個冷門數據被剛剛訪問后,得隔一段時間才會被淘汰(內存污染)。

LFU (Least Frequently Used)算法
LFU 緩存策略是在 LRU 策略基礎上,為每個數據增加了一個計數器,來統計這個數據的訪問次數。
當使用 LFU 策略篩選淘汰數據時,首先會根據數據的訪問次數進行篩選,把訪問次數最低的數據淘汰出緩存。如果兩個數據的訪問次數相同,LFU 策略再比較這兩個數據的訪問時效性,把距離上一次訪問時間更久的數據淘汰出緩存。
LFU 策略使用衰減因子配置項 lfu_decay_time 來控制訪問次數的衰減。
簡單舉個例子,假設 lfu_decay_time 取值為 1,如果數據在 N 分鐘內沒有被訪問,那么它的訪問次數就要減 N。如果 lfu_decay_time 取值更大,那么相應的衰減值會變小,衰減效果也會減弱。所以,如果業務應用中有短時高頻訪問的數據的話,建議把 lfu_decay_time 值設置為 1,這樣一來,LFU 策略在它們不再被訪問后,會較快地衰減它們的訪問次數,盡早把它們從緩存中淘汰出去,避免緩存污染。

淘汰策略使用建議
1,優先使用 allkeys-lru 策略。這樣,可以充分利用 LRU 這一經典緩存算法的優勢,把最近最常訪問的數據留在緩存中,提升應用的訪問性能。如果你的業務數據中有明顯的冷熱數據區分,我建議你使用 allkeys-lru 策略。
2,如果業務應用中的數據訪問頻率相差不大,沒有明顯的冷熱數據區分,建議使用 allkeys-random 策略,隨機選擇淘汰的數據就行。
3,如果你的業務中有置頂的需求,比如置頂新聞、置頂視頻,那么,可以使用 volatile-lru 策略,同時不給這些置頂數據設置過期時間。這樣一來,這些需要置頂的數據一直不會被刪除,而其他數據會在過期時根據 LRU 規則進行篩選。

Redis采用惰性刪除+定期刪除
redis有一個定時任務每秒執行10次。值得注意的是,這里的掃描并不是掃描全部帶有過期時間的key,更像是一種抽查。
第一步,從有失效機制的key中隨機取出20個key。
第二步,刪除已經過期的key。
第三部,判斷下是否超過1/4的key已經失效了,如果沒有執行步驟第一步。
這樣就可以保證在任何時間,過期的key占用的內存空間的最大值 是 每秒寫操作 key/4

惰性刪除策略。當一個數據的過期時間到了以后,并不會立即刪除數據,而是等到再有請求來讀寫這個數據時,對數據進行檢查,如果發現數據已經過期了,再刪除這個數據。
需注意的是從庫本身不會執行刪除操作,如果客戶端在從庫中訪問留存的過期數據,從庫并不會觸發數據刪除。可能讀到過期數據

在 3.2 版本后,Redis 做了改進,如果讀取的數據已經過期了,從庫雖然不會刪除,但是會返回空值,這就避免了客戶端讀到過期數據。所以,在應用主從集群時,盡量使用 Redis 3.2 及以上版本。(主從機器時鐘不一致也可能讀到過期數據)

文章參考:Redis的鍵過期策略、內存淘汰機制與LRU實現,這一篇給你安排了!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容