Redis的逐出算法


Redis使用內存存儲數據,在執行每一個命令前,會調用freeMemoryIfNeeded()檢測內存是否充足。如
果內存不滿足新加入數據的最低存儲要求, redis要臨時刪除一些數據為當前指令清理存儲空間。清理數據
的策略稱為逐出算法。
注意:逐出數據的過程不是100%能夠清理出足夠的可使用的內存空間,如果不成功則反復執行。當對所有數據嘗試完畢后,如果不能達到內存清理的要求,將出現錯誤信息。

(error) OOM command not allowed when used memory >'maxmemory'

影響數據逐出的相關配置
最大可使用內存

maxmemory

占用物理內存的比例,默認值為0,表示不限制。生產環境中根據需求設定,通常設置在50%以上。
每次選取待刪除數據的個數

maxmemory-samples

選取數據時並不會全庫掃描,導致嚴重的性能消耗,降低讀寫性能。因此采用隨機獲取數據的方式作為待檢測刪除數據
刪除策略

maxmemory-policy

達到最大內存后的,對被挑選出來的數據進行刪除的策略
影響數據逐出的相關配置
檢測易失數據(可能會過期的數據集server.db[i].expires )
① volatile-lru:挑選最近最少使用的數據淘汰
② volatile-lfu:挑選最近使用次數最少的數據淘汰
③ volatile-ttl:挑選將要過期的數據淘汰
④ volatile-random:任意選擇數據淘汰
檢測全庫數據(所有數據集server.db[i].dict )
⑤ allkeys-lru:挑選最近最少使用的數據淘汰
⑥ allkeys-lfu:挑選最近使用次數最少的數據淘汰
⑦ allkeys-random:任意選擇數據淘汰
放棄數據驅逐
⑧ no-enviction(驅逐):禁止驅逐數據( redis4.0中默認策略),會引發錯誤OOM( Out Of Memory)

當 Redis 內存超出物理內存限制時,內存的數據會開始和磁盤產生頻繁的交換 (swap)。
交換會讓 Redis 的性能急劇下降,對於訪問量比較頻繁的 Redis 來說,這樣龜速的存取效率基本上等於不可用
在生產環境中我們是不允許 Redis 出現交換行為的,為了限制最大使用內存,Redis 提供了配置參數 maxmemory 來限制內存超出期望大小。
當實際內存超出 maxmemory 時,Redis 提供了幾種可選策略 (maxmemory-policy) 來讓用戶自己決定該如何騰出新的空間以繼續提供讀寫服務。

  1. noeviction 不會繼續服務寫請求 (DEL 請求可以繼續服務),讀請求可以繼續進行。這樣可以保證不會丟失數據,但是會讓線上的業務不能持續進行。這是默認的淘汰策略。
  2. volatile-lru 嘗試淘汰設置了過期時間的 key,最少使用的 key 優先被淘汰。沒有設置過期時間的 key 不會被淘汰,這樣可以保證需要持久化的數據不會突然丟失。
  3. volatile-ttl 跟上面一樣,除了淘汰的策略不是 LRU,而是 key 的剩余壽命 ttl 的值,ttl越小越優先被淘汰。
  4. volatile-random 跟上面一樣,不過淘汰的 key 是過期 key 集合中隨機的 key。
  5. allkeys-lru 區別於 volatile-lru,這個策略要淘汰的 key 對象是全體的 key 集合,而不只是過期的 key 集合。這意味着沒有設置過期時間的 key 也會被淘汰
  6. allkeys-random 跟上面一樣,不過淘汰的策略是隨機的 key。

總結:

volatile-xxx 策略只會針對帶過期時間的 key 進行淘汰,allkeys-xxx 策略會對所有的key 進行淘汰。

如果你只是拿 Redis 做緩存,那應該使用 allkeys-xxx,客戶端寫緩存時不必攜帶過期時間。

如果你還想同時使用 Redis 的持久化功能,那就使用 volatile-xxx策略,這樣可以保留沒有設置過期時間的 key,它們是永久的 key 不會被 LRU 算法淘汰。
LRU 算法
實現 LRU 算法除了需要 key/value 字典外,還需要附加一個鏈表,鏈表中的元素按照
一定的順序進行排列。當空間滿的時候,會踢掉鏈表尾部的元素。當字典的某個元素被訪問
時,它在鏈表中的位置會被移動到表頭。所以鏈表的元素排列順序就是元素最近被訪問的時間順序。
位於鏈表尾部的元素就是不被重用的元素,所以會被踢掉。位於表頭的元素就是最近剛剛被人用過的元素,所以暫時不會被踢。

近似 LRU 算法
Redis 使用的是一種近似 LRU 算法,它跟 LRU 算法還不太一樣。之所以不使用 LRU
算法,是因為需要消耗大量的額外的內存,需要對現有的數據結構進行較大的改造。近似
LRU 算法則很簡單,在現有數據結構的基礎上使用隨機采樣法來淘汰元素,能達到和 LRU
算法非常近似的效果。Redis 為實現近似 LRU 算法,它給每個 key 增加了一個額外的小字
段,這個字段的長度是 24 個 bit,也就是最后一次被訪問的時間戳。
LRU 淘汰不一樣,它的處理方式只有懶惰處理。當 Redis 執行寫操作時,發現內存超出 maxmemory,就會執行一次
LRU 淘汰算法。這個算法也很簡單,就是隨機采樣出 5(可以配置) 個 key,然后淘汰掉最
舊的 key,如果淘汰后內存還是超出 maxmemory,那就繼續隨機采樣淘汰,直到內存低於
maxmemory 為止。
如何采樣就是看 maxmemory-policy 的配置,如果是 allkeys 就是從所有的 key 字典中
隨機,如果是 volatile 就從帶過期時間的 key 字典中隨機。每次采樣多少個 key 看的是
maxmemory_samples 的配置,默認為 5。
下面是隨機 LRU 算法和嚴格 LRU 算法的效果對比圖:

圖中綠色部分是新加入的 key,深灰色部分是老舊的 key,淺灰色部分是通過 LRU 算
法淘汰掉的 key。從圖中可以看出采樣數量越大,近似 LRU 算法的效果越接近嚴格 LRU
算法。同時 Redis3.0 在算法中增加了淘汰池,進一步提升了近似 LRU 算法的效果。
淘汰池是一個數組,它的大小是 maxmemory_samples,在每一次淘汰循環中,新隨機出
來的 key 列表會和淘汰池中的 key 列表進行融合,淘汰掉最舊的一個 key 之后,保留剩余
較舊的 key 列表放入淘汰池中留待下一個循環。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM