聊聊 Redis 內存淘汰策略

這一期我們一塊兒來看看 Redis 的內存淘汰策略~html

爲何要有內存淘汰機制

  你們都知道 Redis 中的鍵會設置過時時間,當到達過時時間時會經過必定策略清除對應 key,可是 redis 內存是由上限的,當達到內存上限時,就要經過必定策略淘汰掉相應 kv 鍵值對。git

Redis 內存上限

maxmemory 配置選項使用來配置 Redis 的存儲數據所能使用的最大內存限制。能夠經過在內置文件redis.conf中配置,也可在Redis運行時經過命令CONFIG SET來配置。例如,咱們要配置內存上限是100M的Redis緩存,那麼咱們能夠在 redis.conf 配置以下:github

maxmemory 100mb
複製代碼

設置 maxmemory 爲 0 表示沒有內存限制。在 64-bit 系統中,默認是 0 無限制,可是在 32-bit 系統中默認是 3GB。redis

當存儲數據達到限制時,Redis 會根據情形選擇不一樣策略,或者返回errors(這樣會致使浪費更多的內存),或者清除一些舊數據回收內存來添加新數據。算法

Redis 內存淘汰策略

  • noenviction:不清除數據,只是返回錯誤,這樣會致使浪費掉更多的內存,對大多數寫命令(DEL 命令和其餘的少數命令例外)
  • allkeys-lru:從全部的數據集中挑選最近最少使用的數據淘汰,以供新數據使用
  • volatile-lru:從已設置過時時間的數據集中挑選最近最少使用的數據淘汰,以供新數據使用
  • allkeys-random:從全部數據集中任意選擇數據淘汰,以供新數據使用
  • volatile-random:從已設置過時時間的數據集中任意選擇數據淘汰,以供新數據使用
  • volatile-ttl:從已設置過時時間的數據集中挑選將要過時的數據淘汰,以供新數據使用
  • volatile-lfu:從全部配置了過時時間的鍵中淘汰使用頻率最少的鍵
  • allkeys-lfu:從全部鍵中淘汰使用頻率最少的鍵

回收的過程

理解回收過程是運做流程很是的重要,回收過程以下:緩存

  • 一個客戶端運行一個新命令,添加了新數據。
  • Redis檢查內存使用狀況,若是大於maxmemory限制,根據策略來回收鍵。
  • 一個新的命令被執行,如此等等。

咱們添加數據時經過檢查,而後回收鍵以返回到限制如下,來接二連三的穿越內存限制的邊界。dom

若是一個命令致使大量的內存被佔用(好比一個很大的集合保存到一個新的鍵),那麼內存限制很快就會被這個明顯的內存量所超越。post

近似LRU算法

Redis的LRU算法不是一個嚴格的LRU實現。這意味着Redis不能選擇最佳候選鍵來回收,也就是最久未被訪問的那些鍵。相反,Redis 會嘗試執行一個近似的LRU算法,經過採樣一小部分鍵,而後在採樣鍵中回收最適合(擁有最久訪問時間)的那個。性能

然而,從Redis3.0開始,算法被改進爲維護一個回收候選鍵池。這改善了算法的性能,使得更接近於真實的LRU算法的行爲。Redis的LRU算法有一點很重要,你能夠調整算法的精度,經過改變每次回收時檢查的採樣數量。url

這個參數能夠經過以下配置指令:

maxmemory-samples 5
複製代碼

Redis沒有使用真實的LRU實現的緣由,是由於這會消耗更多的內存。然而,近似值對使用Redis的應用來講基本上也是等價的。

LFU

LFU (Least frequently used) 最不常用算法。而 LRU 是最近最少使用算法。

從 Redis 4.0 開始,可使用 LFU 過時策略。這種模式在某些狀況下可能會更好(提供更好的命中率/未命中率),由於使用 LFU Redis 會嘗試跟蹤項目的訪問頻率,所以不多使用的項目會被淘汰,而常用的項目有更高的機會留在內存中。

那爲何會出現 LFU 算法那?你們請看下面的場景:

A - A - A - - - A - A -A - - -
B - - - - B - - B - - - - - - B
複製代碼

若是是 LRU 算法 那麼會淘汰 A,由於 B 是最近使用的,可是很明顯 A 的使用頻率是最高的,理應留下 A,因此 LFU 算法應運而生。(淘汰最少使用的 key)

LFU 把原來的 key 對象的內部時鐘的24位分紅兩部分,前16位還表明時鐘,後8位表明一個計數器, 稱爲Morris 計數器。後8位表示當前key對象的訪問頻率,8位只能表明255,可是 redis 並無採用線性上升的方式,而是經過一個複雜的公式,經過配置兩個參數來調整數據的遞增速度。

下圖從左到右表示key的命中次數,從上到下表示影響因子,在影響因子爲100的條件下,通過10M次命中才能把後8位值加滿到255.

factor 100 hits 1000 hits 100K hits 1M hits 10M hits
0 104 255 255 255 255
1 18 49 255 255 255
10 10 18 142 255 255
100 8 11 49 143 255

這個參數是可配置的的,經過這個:

lfu-log-factor 10
複製代碼

上說的是計數器的增加,那麼什麼狀況削減那?

默認是 若是一個 key 每一分鐘沒使用,Morris 計數器 就削減 1. 這個也能夠經過下面進行配置:

lfu-decay-time 1
複製代碼

有個問題就是,新的 key 怎麼辦,豈不是上來就被淘汰?

爲了不這種問題 Redis 默認狀況下新分配的key的後8位計數器的值爲5,防止由於訪問頻率太低而直接被刪除。

總結

Redis 爲了不內存超出容量,使用特定的內存淘汰策略來釋放內存,主要思想是 LRU 和 Redis 4.0 推出的 LFU 算法。LRU 是最近最少使用算法,LFU 是最少使用算法。

若是你以爲這篇文章對你有點用的話,麻煩請給咱們的開源項目點點star:    http://github.crmeb.net/u/defu     不勝感激 !
來自 「開源世界 」 ,連接:    https://ym.baisou.ltd/post/736.html ,如需轉載,請註明出處,不然將追究法律責任。 ​​​​​​