Redis 之 LRU 与 LFU
一、前述
Redis 一直在 Nosql 中占據(jù)著很重要的地位,閱讀官方文檔以及 github 源代碼,是一種非常好的能夠幫助提升的方式,本系列博文主要參考官網(wǎng)翻譯、Github 源代碼以及部分自己的理解而來(lái),如有不準(zhǔn)確或者遺漏,感謝及時(shí)提出改正。
官方文檔:https://redis.io/topics/lru-cache
眾所周知,Redis 是一個(gè)基于內(nèi)存的數(shù)據(jù)庫(kù),因此單線程的讀寫(xiě)避免了頻繁的 cpu 上下文切換開(kāi)銷,同時(shí) redis 也可以用作發(fā)布訂閱、延時(shí)隊(duì)列、分布式鎖等等很多場(chǎng)景,來(lái)實(shí)現(xiàn)我們的業(yè)務(wù)需求,今天分享一下 redis 里的 lru
二、lru 的一些故事
lru 全稱是 Least Recently Used,最久未使用,lru 算法在很多場(chǎng)景都有使用,例如 cpu 調(diào)度,一般常見(jiàn)的 cpu 調(diào)度算法有 FIFO、LRU、時(shí)間片輪轉(zhuǎn)等方法,如果使用的是 LRU ,那么 cpu 就是按照最久未使用的方法來(lái)喚醒線程
三、redis 的 lru
1、Redis 的 lru 指的是內(nèi)存回收清理相關(guān)的最久未使用,但是其實(shí)從Redis 4.0版本開(kāi)始,引入了一個(gè)新的策略 LFU(Least Frequently Used,最不常用的)清除策略
2、Redis 的 maxmemory 配置指令可以為 Redis 的數(shù)據(jù)集指定一定數(shù)值的內(nèi)存。可以通過(guò)在 redis.conf 配置文件配置指令,或者通過(guò) redis-cli 連接 redis ,使用 config set 命令在 redis 運(yùn)行時(shí)來(lái)設(shè)置。例如在 redis.conf 中配置;
maxmemory 100mb
兩種配置方式的區(qū)別:
3、在 64 位系統(tǒng)中,將 maxmemory 設(shè)置為零的時(shí)候,默認(rèn)不會(huì)有內(nèi)存限制,但是在 32 位系統(tǒng)使用 3GB 的隱式內(nèi)存限制,如果達(dá)到了指定的內(nèi)存大小時(shí),可以在不同的策略中進(jìn)行選擇。Redis 只會(huì)返回可能導(dǎo)致更多內(nèi)存被使用的命令的錯(cuò)誤,或者它可以刪除一些舊數(shù)據(jù),以便在每次添加新數(shù)據(jù)時(shí)返回到指定的限制。
4、當(dāng)達(dá)到最大內(nèi)存限制的時(shí)候,可以使用 maxmemory-policy 配置指令配置 Redis 明確的策略,有如下策略:
noeviction:當(dāng)達(dá)到內(nèi)存限制時(shí) redis 服務(wù)端直接返回錯(cuò)誤,客戶端試圖執(zhí)行可能導(dǎo)致更多內(nèi)存被使用的命令(大多數(shù)寫(xiě)命令,但是DEL和其他一些異常)。allkeys-lru:嘗試刪除最久未使用的 key,以便給新數(shù)據(jù)的加如騰出空間volatile-lru:在那些設(shè)置了過(guò)期時(shí)間的 key 集合里刪除最久未使用的 keyallkeys-random:在所有的 key 里隨機(jī)淘汰volatile-random:在設(shè)置了過(guò)期時(shí)間的 key 集合里,隨機(jī)淘汰volatile-ttl:在設(shè)置了過(guò)期時(shí)間的 key 集合里,首先嘗試淘汰最短存活時(shí)間 (TTL) 的 key這里值得注意的是,如果沒(méi)有符合前提條件的 key 被淘汰,那么 volatile-lru、volatile-random 、volatile-ttl 相當(dāng)于 noeviction
5、根據(jù)應(yīng)用程序的訪問(wèn)規(guī)則選擇正確的 key 淘汰策略是非常重要的,但是開(kāi)發(fā)者可以在應(yīng)用程序運(yùn)行時(shí)重新配置策略,并使用 Redis 的 info 指令輸出監(jiān)視緩存丟失和命中的次數(shù),以此做對(duì)應(yīng)的優(yōu)化,一般遵循如下規(guī)則:
1)當(dāng)你預(yù)計(jì)你的應(yīng)用程序里的絕大部分請(qǐng)求是滿足冪律的,就使用 allkeys-lru,也就是說(shuō)預(yù)計(jì)元素的子集被訪問(wèn)的頻率將遠(yuǎn)遠(yuǎn)高于其他的元素。當(dāng)你不確定的時(shí)候,這種策略是一個(gè)不錯(cuò)的選擇
2) 如果應(yīng)用程序的訪問(wèn)請(qǐng)求是輪詢的,內(nèi)存里的所有 key 都是被連續(xù)掃描的,或者預(yù)計(jì)的請(qǐng)求分布式均勻的(也就是說(shuō),每個(gè)可能被訪問(wèn)到的元素有著相同的概率被訪問(wèn)),可以使用 allkeys-random
3)如果想告訴 redis ,在創(chuàng)建緩存對(duì)象的時(shí)候,通過(guò)使用不同的 TTL 值來(lái)確定哪些是即將過(guò)期的對(duì)象,使用 volatile-ttl
volatile-lru 和 volatile-random 策略主要適用于同時(shí)使用一個(gè)實(shí)例進(jìn)行緩存和持久化鍵的情況。但是一般來(lái)說(shuō)運(yùn)行兩個(gè) Redis 實(shí)例來(lái)解決這樣的問(wèn)題通常更好。
同樣值得注意的是,將過(guò)期值設(shè)置為鍵值會(huì)消耗內(nèi)存,所以使用 allkeys-lru 這樣的策略會(huì)提高內(nèi)存效率,因?yàn)樵趦?nèi)存壓力下不需要為要清除的鍵設(shè)置過(guò)期值。因?yàn)樵谔蕴^(guò)期的 key 的時(shí)候,是需要消耗內(nèi)存的。
三、內(nèi)存淘汰是如何進(jìn)行的
上面介紹完了六種淘汰策略以后,接下來(lái)我們看看 redis 的內(nèi)存淘汰是怎樣的一個(gè)過(guò)程:
(1)客戶端運(yùn)行了一個(gè)新的命令,來(lái)添加更多的數(shù)據(jù) (2)Redis 檢查內(nèi)存的使用情況,如果發(fā)現(xiàn)大于了 maxmemory 設(shè)置的最大的內(nèi)存,就執(zhí)行當(dāng)前設(shè)置的內(nèi)存淘汰策略 (3)執(zhí)行一個(gè)新的命令,以此類推四、Redis 的 LRU
1、Redis LRU 算法并不是一個(gè)內(nèi)存淘汰準(zhǔn)確的實(shí)現(xiàn),因?yàn)闀?huì)存在這樣的一種情況,一個(gè) key 在過(guò)去的一段時(shí)間都沒(méi)有被訪問(wèn),但是在臨近內(nèi)存淘汰的時(shí)候,突然被調(diào)用一次,這樣,在內(nèi)存淘汰的時(shí)候,這個(gè) key 就很可能不被淘汰,意味著 Redis 不能選擇到最佳的淘汰候選對(duì)象,也就是過(guò)去一段時(shí)間訪問(wèn)次數(shù)最多的 key。它將嘗試運(yùn)行 LRU 算法的近似實(shí)現(xiàn),就是是對(duì)少量密鑰進(jìn)行采樣,并在采樣的密鑰中剔除最合適的的(具有最久的訪問(wèn)時(shí)間)
2、然而,自從 Redis 3.0 版本以來(lái),這種算法得到了改進(jìn),同時(shí)也獲得了一些更加符合的候選淘汰對(duì)象,提高了算法的性能,使其能夠更接近真實(shí) LRU 算法的策略
3、Redis LRU 算法的重要之處在于可以通過(guò)更改樣本數(shù)量來(lái)檢查每次淘汰,從而調(diào)整算法的精確度,通過(guò)使用配置maxmemory-samples 命令在生產(chǎn)環(huán)境中對(duì)不同的樣本量值進(jìn)行實(shí)驗(yàn)是非常簡(jiǎn)單的,該參數(shù)由以下配置指令控制:
Redis 不使用真正的 lru 算法是因?yàn)樗枰嗟膬?nèi)存支持
4、在一個(gè)理論的 LRU 實(shí)現(xiàn)中,預(yù)計(jì)在舊的 key中,前一半將過(guò)期。Redis LRU 算法將只在概率上過(guò)期的 key,與Redis 2.8相比,Redis 3.0在處理 5 個(gè)對(duì)象時(shí)做得更好,但是最新訪問(wèn)的大多數(shù)對(duì)象仍然會(huì)被 Redis 2.8 保留。在 Redis 3.0中 使用10個(gè)樣本大小的近似非常接近于Redis 3.0的理論性能。
五、Redis 的 LFU
1、從 Redis 4.0 版本開(kāi)始,可以使用一種新的最少使用的回收模式。在某些情況下,這種模式可能工作得更好(提供更好的命中率/失誤率),因?yàn)槭褂?LFU Redis 將嘗試跟蹤訪問(wèn)項(xiàng)的頻率,這樣很少使用的 key 將被清除,而使用的項(xiàng)通常有更高的機(jī)會(huì)留在內(nèi)存中。
2、之前提到過(guò),在 LRU 中,一個(gè)最近訪問(wèn)過(guò)但實(shí)際上幾乎從未被請(qǐng)求過(guò)的 key 很有可能不會(huì)過(guò)期,那么風(fēng)險(xiǎn)就是刪除一個(gè)將來(lái)有更高概率被請(qǐng)求的 key。LFU 沒(méi)有這個(gè)問(wèn)題,可以更好地適應(yīng)不同的訪問(wèn)模式。
3、配置 LFU 策略,如下:
(1)volatile-lfu :在設(shè)置了失效時(shí)間的所有 key 中,使用近似的 LFU 淘汰 key,也就是最少被訪問(wèn)的 key (2)allkeys-lfu : 在所有 key 里根據(jù) LFU 淘汰 key4、LFU 近似像 LRU:它使用概率計(jì)數(shù)器,稱為 莫里斯計(jì)數(shù)器 ,為了估計(jì)對(duì)象訪問(wèn)頻率使用少數(shù)的比特位計(jì)算,結(jié)合衰減期,計(jì)數(shù)器是隨時(shí)間減少的:在某種程度上我們不再需要考慮 key 是不是經(jīng)常訪問(wèn),即使他們?cè)谶^(guò)去,這樣的算法可以適應(yīng)訪問(wèn)模式的轉(zhuǎn)變
5、然而,與 LRU 不同的是,LFU 具有某些可調(diào)參數(shù):例如,如果不再訪問(wèn)一個(gè)頻繁項(xiàng),那么它的級(jí)別應(yīng)該降低到多快?還可以調(diào)優(yōu) Morris 計(jì)數(shù)器范圍,以便更好地使算法適應(yīng)特定的用例
Redis 4.0 默認(rèn)的配置:
有關(guān)如何調(diào)優(yōu)這些參數(shù)的說(shuō)明可以在 redis.conf 文件中找到,簡(jiǎn)單地說(shuō),如下所示:
(1)lfu-log-factor 10 可以調(diào)整計(jì)數(shù)器counter的增長(zhǎng)速度,lfu-log-factor越大,counter增長(zhǎng)的越慢 (2)lfu-decay-time 1 是一個(gè)以分鐘為單位的數(shù)值,可以調(diào)整counter的減少速度衰減時(shí)間默認(rèn)是1,它是計(jì)數(shù)器應(yīng)該衰減的分鐘數(shù),長(zhǎng)時(shí)間不讀取key的話,是需要進(jìn)行衰減的,當(dāng)每次采樣發(fā)現(xiàn)時(shí)計(jì)數(shù)器的時(shí)間比這個(gè)值要大。有一個(gè)特殊的值 ,0 表示每次掃描時(shí)計(jì)數(shù)器總是衰減,很少用到
計(jì)數(shù)器對(duì)數(shù)因子改變了需要多少次命中才能使頻率計(jì)數(shù)器飽和,而頻率計(jì)數(shù)器剛好在 0-255 范圍內(nèi)。因子越大,為了達(dá)到最大,需要的訪問(wèn)次數(shù)越多。因子越低,低訪問(wèn)的計(jì)數(shù)器分辨率越好
總結(jié)
以上是生活随笔為你收集整理的Redis 之 LRU 与 LFU的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AS 3.6 稳定版终于发布了,新版本带
- 下一篇: Mybatis | Mybatis 一级