1. 程式人生 > 其它 >聊聊 Redis 記憶體淘汰策略

聊聊 Redis 記憶體淘汰策略

這一期咱們一起來看看 Redis 的記憶體淘汰策略~

為什麼要有記憶體淘汰機制

大家都知道 Redis 中的鍵會設定過期時間,當到達過期時間時會通過一定策略清除對應 key,但是 redis 記憶體是由上限的,當達到記憶體上限時,就要通過一定策略淘汰掉相應 kv 鍵值對。

Redis 記憶體上限

maxmemory 配置選項使用來配置 Redis 的儲存資料所能使用的最大記憶體限制。可以通過在內建檔案redis.conf中配置,也可在Redis執行時通過命令CONFIG SET來配置。例如,我們要配置記憶體上限是100M的Redis快取,那麼我們可以在 redis.conf 配置如下:

maxmemory 100mb
複製程式碼

設定 maxmemory 為 0 表示沒有記憶體限制。在 64-bit 系統中,預設是 0 無限制,但是在 32-bit 系統中預設是 3GB。

當儲存資料達到限制時,Redis 會根據情形選擇不同策略,或者返回errors(這樣會導致浪費更多的記憶體),或者清除一些舊資料回收記憶體來新增新資料。

Redis 記憶體淘汰策略

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

回收的過程

理解回收過程是運作流程非常的重要,回收過程如下:

  • 一個客戶端執行一個新命令,添加了新資料。
  • Redis檢查記憶體使用情況,如果大於maxmemory限制,根據策略來回收鍵。
  • 一個新的命令被執行,如此等等。

我們新增資料時通過檢查,然後回收鍵以返回到限制以下,來連續不斷的穿越記憶體限制的邊界。

如果一個命令導致大量的記憶體被佔用(比如一個很大的集合儲存到一個新的鍵),那麼記憶體限制很快就會被這個明顯的記憶體量所超越。

近似LRU演算法

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

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

這個引數可以通過如下配置指令:

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.

factor100 hits1000 hits100K hits1M hits10M 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,如需轉載,請註明出處,否則將追究法律責任。 ​​​​​​