Redis 內存淘汰機制詳解

通常來講,緩存的容量是小於數據總量的,因此,當緩存數據愈來愈多,Redis 不可避免的會被寫滿,這時候就涉及到 Redis 的內存淘汰機制了。咱們須要選定某種策略將「不重要」的數據從 Redis 中清除,爲新的數據騰出空間。redis

配置 Redis 內存大小

咱們應該爲 Redis 設置多大的內存容量呢?算法

根據「八二原理「,即 80% 的請求訪問了 20% 的數據,所以若是按照這個原理來配置,將 Redis 內存大小設置爲數據總量的 20%,就有可能攔截到 80% 的請求。固然,只是有可能,對於不一樣的業務場景須要進行不一樣的配置,通常建議把緩存容量設置爲總數據量的 15% 到 30%,兼顧訪問性能和內存空間開銷shell

配置方式(以 5GB 爲例,若是不帶單位則默認單位是字節):緩存

  • 命令行數據結構

    config set maxmemory 5gb
  • 配置文件dom

  • 查看 maxmemory 命令性能

    config get maxmemory

Redis 的內存淘汰策略

在 Redis 4.0 版本以前有 6 種策略,4.0 增長了 2種,主要新增了 LFU 算法。命令行

下圖爲 Redis 6.2.0 版本的配置文件:code

其中,默認的淘汰策略是 noevition,也就是不淘汰內存

咱們能夠對 8 種淘汰策略能夠分爲兩大類:

  • 不進行淘汰的策略

    • noevition,此策略不會對緩存的數據進行淘汰,當內存不夠了就會報錯,所以,若是真實數據集大小大於緩存容量,就不要使用此策略了。

  • 會進行淘汰的策略

    • 在設置了過時時間的數據中篩選
      • volatile-random:隨機刪除
      • volatile-ttl:根據過時時間前後進行刪除,越早過時的越先被刪除
      • volatile-lru:使用 LRU 算法進行篩選刪除
      • volatile-lfu:使用 LFU 算法進行篩選刪除
    • 在全部數據中篩選
      • allkeys-random:隨機刪除
      • allkeys-lru:使用 LRU 算法進行篩選刪除
      • allkeys-lfu:使用 LFU 算法進行篩選刪除

以 volatile 開頭的策略只針對設置了過時時間的數據,即便緩存沒有被寫滿,若是數據過時也會被刪除。

以 allkeys 開頭的策略是針對全部數據的,若是數據被選中了,即便過時時間沒到,也會被刪除。固然,若是它的過時時間到了但未被策略選中,一樣會被刪除。

那麼咱們如何配置過時策略呢?

  • 命令行

    config set maxmemory-policy allkeys-lru
  • 配置文件

LRU 算法

首先簡單介紹一下 LRU 算法:

LRU 全稱是 Least Recently Used,即最近最少使用,會將最不經常使用的數據篩選出來,保留最近頻繁使用的數據。

LRU 會把全部數據組成一個鏈表,鏈表頭部稱爲 MRU,表明最近最常使用的數據;尾部稱爲 LRU表明最近最不常使用的數據;

下圖是一個簡單的例子:

可是,若是直接在 Redis 中使用 LRU 算法也會有一些問題:

LRU 算法在實現過程當中使用鏈表管理全部緩存的數據,這會給 Redis 帶來額外的開銷,並且,當有數據訪問時就會有鏈表移動操做,進而下降 Redis 的性能。

因而,Redis 對 LRU 的實現進行了一些改變:

  • 記錄每一個 key 最近一次被訪問的時間戳(由鍵值對數據結構 RedisObject 中的 lru 字段記錄)
  • 在第一次淘汰數據時,會先隨機選擇 N 個數據做爲一個候選集合,而後淘汰 lru 值最小的。(N 能夠經過 config set maxmemory-samples 100 命令來配置)
  • 後續再淘汰數據時,會挑選數據進入候選集合,進入集合的條件是:它的 lru 小於候選集合中最小的 lru。
  • 若是候選集合中數據個數達到了 maxmemory-samples,Redis 就會將 lru 值小的數據淘汰出去。

LFU 算法

LFU 全稱 Least Frequently Used,即最不常常使用策略,它是基於數據訪問次數來淘汰數據的,在 Redis 4.0 時添加進來。它在 LRU 策略基礎上,爲每一個數據增長了一個計數器,來統計這個數據的訪問次數。

前面說到,LRU 使用了 RedisObject 中的 lru 字段記錄時間戳,lru 是 24bit 的,LFU 將 lru 拆分爲兩部分:

  • ldt 值:lru 字段的前 16bit,表示數據的訪問時間戳
  • counter 值:lru 字段的後 8bit,表示數據的訪問次數

使用 LFU 策略淘汰緩存時,會把訪問次數最低的數據淘汰,若是訪問次數相同,再根據訪問的時間,將訪問時間戳最小的淘汰。

爲何 Redis 有了 LRU 還須要 LFU 呢?

在一些場景下,有些數據被訪問的次數很是少,甚至只會被訪問一次。當這些數據服務完訪問請求後,若是還繼續留存在緩存中的話,就只會白白佔用緩存空間。

因爲 LRU 是基於訪問時間的,若是系統對大量數據進行單次查詢,這些數據的 lru 值就很大,使用 LFU 算法就不容易被淘汰。

參考