redis(十二:淘汰機(jī)制)

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


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

Redis 緩存有哪些淘汰策略

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

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

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

以上的淘汰機(jī)制都是定時刪除沒掃描到的老賴,當(dāng)內(nèi)存不足時,進(jìn)行淘汰。

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


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

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

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

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

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

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

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

文章參考:Redis的鍵過期策略、內(nèi)存淘汰機(jī)制與LRU實(shí)現(xiàn),這一篇給你安排了!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容