Redis 記憶體淘汰機制詳解
阿新 • • 發佈:2021-02-26
一般來說,快取的容量是小於資料總量的,所以,當快取資料越來越多,Redis 不可避免的會被寫滿,這時候就涉及到 Redis 的記憶體淘汰機制了。我們需要選定某種策略將“不重要”的資料從 Redis 中清除,為新的資料騰出空間。
# 配置 Redis 記憶體大小
我們應該為 Redis 設定多大的記憶體容量呢?
根據“八二原理“,即 80% 的請求訪問了 20% 的資料,因此如果按照這個原理來配置,將 Redis 記憶體大小設定為資料總量的 20%,就有可能攔截到 80% 的請求。當然,只是有可能,對於不同的業務場景需要進行不同的配置,一般**建議把快取容量設定為總資料量的 15% 到 30%,兼顧訪問效能和記憶體空間開銷**。
**配置方式**(以 5GB 為例,如果不帶單位則預設單位是位元組):
- 命令列
```shell
config set maxmemory 5gb
```
- 配置檔案
![](https://gitee.com/songjilong/picbed/raw/master/img/20210226111635.png)
- 檢視 maxmemory 命令
```shell
config get maxmemory
```
# Redis 的記憶體淘汰策略
在 Redis 4.0 版本之前有 6 種策略,4.0 增加了 2種,主要新增了 LFU 演算法。
下圖為 Redis 6.2.0 版本的配置檔案:
![](https://gitee.com/songjilong/picbed/raw/master/img/20210226152618.png)
其中,預設的淘汰策略是 noevition,也就是不淘汰
我們可以對 8 種淘汰策略可以分為兩大類:
- **不進行淘汰的策略**
- noevition,此策略不會對快取的資料進行淘汰,當記憶體不夠了就會報錯,因此,如果真實資料集大小大於快取容量,就不要使用此策略了。
![](https://gitee.com/songjilong/picbed/raw/master/img/20210226152952.png)
- **會進行淘汰的策略**
- **在設定了過期時間的資料中篩選**
- volatile-random:隨機刪除
- volatile-ttl:根據過期時間先後進行刪除,越早過期的越先被刪除
- volatile-lru:使用 LRU 演算法進行篩選刪除
- volatile-lfu:使用 LFU 演算法進行篩選刪除
- **在所有資料中篩選**
- allkeys-random:隨機刪除
- allkeys-lru:使用 LRU 演算法進行篩選刪除
- allkeys-lfu:使用 LFU 演算法進行篩選刪除
> 以 volatile 開頭的策略只針對設定了過期時間的資料,即使快取沒有被寫滿,如果資料過期也會被刪除。
>
> 以 allkeys 開頭的策略是針對所有資料的,如果資料被選中了,即使過期時間沒到,也會被刪除。當然,如果它的過期時間到了但未被策略選中,同樣會被刪除。
那麼我們如何配置過期策略呢?
- 命令列
```shell
config set maxmemory-policy allkeys-lru
```
- 配置檔案
![](https://gitee.com/songjilong/picbed/raw/master/img/20210226154130.png)
# LRU 演算法
**首先簡單介紹一下 LRU 演算法:**
LRU 全稱是 Least Recently Used,即最近最少使用,會將最不常用的資料篩選出來,保留最近頻繁使用的資料。
LRU 會把所有資料組成一個連結串列,連結串列頭部稱為 MRU,代表最近最常使用的資料;尾部稱為 LRU代表最近最不常使用的資料;
**下圖是一個簡單的例子:**
![](https://gitee.com/songjilong/picbed/raw/master/img/20210226154237.png)
**但是,如果直接在 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 演算法就不容易被淘汰。
# 參考
- 極客時間[《Redis核心技術與實戰》](https://time.geekbang.org/column/intro/100056701)