Redis系列11:記憶體淘汰策略

2022-11-21 12:02:08

Redis系列1:深刻理解高效能Redis的本質
Redis系列2:資料持久化提高可用性
Redis系列3:高可用之主從架構
Redis系列4:高可用之Sentinel(哨兵模式)
Redis系列5:深入分析Cluster 叢集模式
追求效能極致:Redis6.0的多執行緒模型
追求效能極致:使用者端快取帶來的革命
Redis系列8:Bitmap實現億萬級資料計算
Redis系列9:Geo 型別賦能億級地圖位置計算
Redis系列10:HyperLogLog實現海量資料基數統計

1 前言

通過前面的一些文章我們知道,Redis的各項能力是基於記憶體實現的,相對其他的持久化儲存(如MySQL、File等,資料持久化在磁碟上),效能會高很多,這也是快取記憶體的一個優勢。
但是問題來了,每一臺機器記憶體終歸是有限的,即使是叢集模式,總的記憶體空間也是有限的,不能無限制的消耗。而在Redis的使用過程中,很有可能出現使用消耗超過記憶體實際大小的情況。比如以下幾種情況:

  • 未設定過期時間,Redis的Key將一直存在,直至我們明確將它刪除。
  • 過度跟不合理的持久化(無論是RDB快照 或是 AOF紀錄檔),都會在記憶體和磁碟中反覆操作,需要一定的記憶體空間進行處理。
  • 不及時清理過期快取:清理過期快取的方式主要有以下兩種,並不是實時或者準實時,所以存在部分過期快取依舊存在的問題。
    • 主動定期刪除: Redis 預設每 1 秒執行 10 次(平均每 100 ms 執行一次),每次隨機抽取部分設定過期時間的 key,檢查是否過期,若是過期就直接刪除,直至過期的 key 比率低於 1/4。
    • 被動惰性刪除:快取過期並不馬上清理,當用戶端的請求查詢該 key 的時候,檢查下 key 是否過期,如果過期,則刪除該 key,重新獲取。如果長時間未請求,就會有過期快取滯留。
  • 不合理不規範的使用快取,導致記憶體耗盡,比如:
    • 過度使用快取,既快取冷資料也能快取熱資料,導致記憶體佔用過多,效能也沒有得到有效提高
    • 快取數量過多或者單個快取的Value體積過大
    • 快取過期時間設定過長或者根本不設定

2 Redis記憶體淘汰策略

所以,如果放任上面的那幾種情況,記憶體終歸會滿的,Redis自身有一套比較完善的記憶體淘汰策略來專門應對這個問題,在Redis Memory佔用超過我們設定的閾值的時候觸發策略執行。

# redis.conf 設定最大記憶體空間佔用為2gb,超過則執行記憶體淘汰策略
redis > CONFIG SET maxmemory 2gb

記憶體淘汰策略一共有8中,除了一種不執行淘汰策略之外,其他7種都是按照各自不一的演演算法對記憶體中現有的資料進行處理。
我們下面詳細來看一下這些淘汰策略,把他們分成三大類,8小類來逐一講解:

2.1 不淘汰策略

2.1.1 noeviction 不淘汰策略

noeviction指的是即使資源超過 maxmemory 限制的值也不會執行淘汰,只是不允許建立新的快取了。
當Redis記憶體佔用達到我們上面的設定的閾值(比如 5gb)之後,就不允許新增快取key了,當有新的快取要建立的時候,Redis 直接返回error。

2.2 僅淘汰設定過期時間key

這邊僅針對設定了過期時間的資料進行淘汰

2.3.1 volatile-lru :刪除最近最少使用的key

LRU(Least Recently Used)是按照最近最少使用原則來篩選資料,即最不常用的資料會被篩選出來。
如果我們的服務中有冷熱資料隔離需求,這無疑是一個比較好的辦法。可以將快取的一些不經常使用的冷資料,而且資料size比較大的,篩選出來清理掉。而近期頻繁被使用的key就被保留下來了。
常見的場景如下:

  • 電商平臺的冷熱資料:比如冬季,保暖冬裝、電暖裝置的瀏覽次數就會升高,而相應的冷飲、製冷裝置(冰箱、空調)的瀏覽次數就會降低,那麼LRU策略下優先刪除的就是最近一段時間未存取的快取資訊。
  • 外賣平臺:每天的1113點,1719點,一定是美食外賣品種的高頻率存取時間段,而日用品、果蔬生鮮 大都會避開這個高峰期,這時如果記憶體不夠用了,那麼就會成為被優先刪除的快取型別。

2.3.2 volatile-lfu:刪除存取次數最少的key(4.0 之後新增的策略)

LRU演演算法的不足之處在於,一個本身很少被存取的key,只是剛剛被存取了1次,就被認為是最近有使用的熱點資料,導致短時間內不會被淘汰。
而LFU彌補了這個不足,LFU(Least Frequently Used)淘汰策略會根據key的最近存取頻率進行淘汰,解決上面說的這個不足。

  • LFU在LRU的基礎上,為每個資料增加了一個計數器,用於統計該資料的存取次數。
  • 當使用LFU策略淘汰資料時,會根據資料的存取次數進行篩選,把存取次數最低的資料淘汰出記憶體。
  • 如果兩個快取資料的存取次數相同,LFU再比較這兩個key最近一次的存取時間,把存取時間更早的快取key淘汰出記憶體。

常見的應用場景:

  • 對於電商平臺中的冷門的商品,電子書App中熱度較低、閱讀量較低的書籍。這種型別的快取會優先被淘汰掉。

2.3.3 volatile-random:隨機刪除過期key

針對有設定過期時間,但沒有明顯的冷熱存取頻率區別,所有的查詢分佈比較均衡的資料。這時候就使用 allkeys-random 策略吧,讓它隨機選擇需要淘汰資料,也相對公平。
常見的使用場景有:

  • 電商平臺:常規時段的商品瀏覽。
  • 釘釘之類工具:老師無差別抽查學生的作業。

2.3.4 volatile-ttl:刪除過期時間內剩餘時間最短的key

這個特性僅限於設定過期時間的場景,它是根據當前時間 跟 過期時間的差額進行由短到長的排序,較短的優先淘汰。

 asc_sort(validate_time - current_time)

這種演演算法相對來說也不考慮快取的存取頻率和重要程度,僅按照建立的先後進行清理,越早的快取越早清理。
所以不具備明顯特徵的業務場景都適用。

2.3.5 補充說明

業務場景有一些資料始終不需要刪除,比如置頂新聞、視訊,還有我們自己置頂的weibo。為了保障它們不被清理掉,就給這些資料不設定過期時間,這樣的話 volatile型別的淘汰策略就不會影響了。但如果是 allkeys 開頭的策略依舊會影響到。

2.3 淘汰所有快取型別的key

無論是否設定了過期時間的資料均可進行淘汰。
從微服務拆分的角度說,不同的服務型別個方向的服務進行院子隔離會比較一點。這一點設計思維在快取上依舊適用。
我們可以將不需要過期時間的快取資訊 和 需強制設定過期時間的快取key分開。針對業務場景分別使用 volatile-xx策略 和 allkyes-xxx策略。

2.3.1 allkeys-lru:刪除最近最少使用的key

保留最近有使用的key,類似volatile-lru

2.3.2 allkeys-lfu:刪除存取次數最少的key

最不經常使用的,類似volatile-lfu

2.3.3 allkeys-random:隨機刪除過期key

無差別隨機刪除,volatile-random,為新增新資料騰出空間

2.4 策略命令的使用

# 獲取當前記憶體淘汰策略
redis > config get maxmemory-policy

# 獲取Redis能使用的最大記憶體大小:如果不設定最大記憶體大小或者設定最大記憶體大小為0,在64位元作業系統下不限制記憶體大小,在32位元作業系統下最多使用3GB記憶體。
redis > config get maxmemory

#  通過命令設定淘汰策略
redis > config set maxmemory-policy volatile-lru

# 設定Redis最大佔用記憶體大小,這邊最大佔用記憶體大小設定為2000M
redis > config set maxmemory 2000mb

3 總結

一張圖總結