1. 程式人生 > 其它 >Redis這個記憶體回收,確實有點牛逼!!!

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

本文由傳智教育博學谷教研團隊釋出。

如果本文對您有幫助,歡迎關注點贊;如果您有任何建議也可留言評論私信,您的支援是我堅持創作的動力。

轉載請註明出處!