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