Redis這個記憶體回收,確實有點牛逼!!!
1. 過期 key 處理
Redis 之所以效能強,最主要的原因就是基於記憶體儲存。然而單節點的 Redis 其記憶體大小不宜過大,會影響持久化或主從同步效能。
我們可以通過修改配置檔案來設定 Redis 的最大記憶體:
maxmemory 1gb
當記憶體使用達到上限時,就無法儲存更多資料了。為了解決這個問題,Redis 提供了一些策略實現記憶體回收:
先要了解的是:redis 是一個儲存鍵值資料庫系統,那它原始碼中是如何儲存所有鍵值對的呢?
Redis 本身是一個典型的 key-value 記憶體儲存資料庫,因此所有的 key、value 都儲存在之前學習過的 Dict 結構中。不過在其 database 結構體中,有兩個 Dict:一個用來記錄 key-value;另一個用來記錄 key-TTL。
內部結構
- dict 是 hash 結構,用來存放所有的 鍵值對
- expires 也是 hash 結構,用來存放所有設定了 過期時間的 鍵值對,不過它的 value 值是過期時間
這裡有兩個問題需要我們思考:
- Redis 是如何知道一個 key 是否過期呢?
- 利用兩個 Dict 分別記錄 key-value 對及 key-ttl 對,是不是 TTL 到期就立即刪除了呢?
總結:Redis的過期刪除策略就是:惰性刪除和定期刪除兩種策略配合使用
惰性刪除
惰性刪除:顧明思議並不是在 TTL 到期後就立刻刪除,而是在訪問一個 key 的時候,檢查該 key 的存活時間,如果已經過期才執行刪除。
週期刪除
週期刪除:顧明思議是通過一個定時任務,週期性的抽樣部分過期的 key,然後執行刪除。執行週期有兩種:
- Redis 服務初始化函式 initServer () 中設定定時任務,按照 server.hz 的頻率來執行過期 key 清理,模式為 SLOW
- Redis 的每個事件迴圈前會呼叫 beforeSleep () 函式,執行過期 key 清理,模式為 FAST
SLOW 模式規則:
- 執行頻率受 server.hz 影響,預設為 10,即每秒執行 10 次,每個執行週期 100ms。
- 執行清理耗時不超過一次執行週期的 25%. 預設 slow 模式耗時不超過 25ms
- 逐個遍歷 db,逐個遍歷 db 中的 bucket,抽取 20 個 key 判斷是否過期
- 如果沒達到時間上限(25ms)並且過期 key 比例大於 10%,再進行一次抽樣,否則結束
FAST 模式規則(過期 key 比例小於 10% 不執行 ):
- 執行頻率受 beforeSleep () 呼叫頻率影響,但兩次 FAST 模式間隔不低於 2ms
- 執行清理耗時不超過 1ms
- 逐個遍歷 db,逐個遍歷 db 中的 bucket,抽取 20 個 key 判斷是否過期
- 如果沒達到時間上限(1ms)並且過期 key 比例大於 10%,再進行一次抽樣,否則結束
2記憶體淘汰策略
①、設定Redis最大記憶體
在配置檔案redis.conf 中,可以通過引數 maxmemory
不設定該引數預設是無限制的,但是通常會設定其為實體記憶體的四分之三
②、設定記憶體淘汰方式
當現有記憶體大於 maxmemory 時,便會觸發redis主動淘汰記憶體方式,通過設定 maxmemory-policy
有如下幾種淘汰方式:
-
volatile-lru
:設定了過期時間的key使用LRU演算法淘汰; -
allkeys-lru
:所有key使用LRU演算法淘汰; -
volatile-lfu
:設定了過期時間的key使用LFU演算法淘汰; -
allkeys-lfu
:所有key使用LFU演算法淘汰; -
volatile-random
:設定了過期時間的key使用隨機淘汰; -
allkeys-random
:所有key使用隨機淘汰; -
volatile-ttl
:設定了過期時間的key根據過期時間淘汰,越早過期越早淘汰; -
noeviction
:預設策略,當記憶體達到設定的最大值時,所有申請記憶體的操作都會報錯(如set,lpush等),只讀操作如get命令可以正常執行;
比較容易混淆的有兩個:
LRU(Least Recently Used),最少最近使用。用當前時間減去最後一次訪問時間,這個值越大則淘汰優先順序越高。
LFU(Least Frequently Used),最少頻率使用。會統計每個 key 的訪問頻率,值越小淘汰優先順序越高。
* LRU、LFU和volatile-ttl都是近似隨機演算法;
使用下面的引數maxmemory-policy配置淘汰策略:
#配置檔案
maxmemory-policy noeviction
#命令列
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> config set maxmemory-policy allkeys-random
OK
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "allkeys-random"
Redis 的資料都會被封裝為 RedisObject 結構:
LFU 的訪問次數之所以叫做邏輯訪問次數,是因為並不是每次 key 被訪問都計數,而是通過運算:
- 生成 0~1 之間的隨機數 R
- 計算 (舊次數 * lfu_log_factor + 1),記錄為 P
- 如果 R < P ,則計數器 + 1,且最大不超過 255
- 訪問次數會隨時間衰減,距離上一次訪問時間每隔 lfu_decay_time 分鐘,計數器 -1
本文由
傳智教育博學谷
教研團隊釋出。如果本文對您有幫助,歡迎
關注
和點贊
;如果您有任何建議也可留言評論
或私信
,您的支援是我堅持創作的動力。轉載請註明出處!