將 Redis 用作快取時, 如果記憶體空間用滿, 就會自動驅逐老的資料。預設 memcached 就是這種方式, 大部分開發者都比較熟悉。 (推薦學習:Redis影片教學)
LRU是Redis唯一支援的回收演算法. 本文詳細介紹用於限制最大記憶體使用量的maxmemory 指令, 並深入講解Redis 所使用的近似LRU演算法。
maxmemory 設定指令
maxmemory 用來指定 Redis 能使用的最大記憶體。既可以在 redis.conf 檔案中設定, 也可以在運行過程中透過 CONFIG SET 指令動態修改。
例如, 要設定 100MB 的記憶體限制, 可以在 redis.conf 檔案中這樣配置:
maxmemory 100mb
將 maxmemory 設定為 0, 則表示不進行記憶體限制。當然, 對32位元系統來說有一個隱性的限制條件: 最多 3GB 記憶體。
當記憶體使用達到最大限制時, 如果需要儲存新資料, 根據配置的策略(policies)的不同, Redis可能直接傳回錯誤訊息, 或刪除部分老的資料。
驅逐策略
達到最大記憶體限制時(maxmemory), Redis 根據 maxmemory-policy 配置的策略, 來決定具體的行為。
目前版本,Redis 3.0 支援的策略包括:
noeviction: 不刪除策略, 達到最大記憶體限制時, 如果需要更多記憶體, 直接傳回錯誤訊息。大多數寫指令都會導致佔用更多的記憶體(有極少數會例外, 如 DEL )。
allkeys-lru: 所有key通用; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
volatile-lru: 只限於設定了 expire 的部分; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
allkeys-random: 所有key通用; 隨機刪除一部分 key。
volatile-random: 只限於設定了 expire 的部分; 隨機刪除一部分 key。
volatile-ttl: 只限於設定了 expire 的部分; 優先刪除剩餘時間(time to live,TTL) 短的key。
如果沒有設定 expire 的key, 不滿足先決條件(prerequisites); 那麼 volatile-lru, volatile-random 和 volatile-ttl 策略的行為, 和 noeviction(不刪除) 基本上一致。
您需要根據系統的特徵, 來選擇合適的驅逐策略。當然, 在運行過程中也可以透過命令動態設定驅逐策略, 並透過 INFO 指令監控快取的 miss 和 hit, 來進行調優。
一般來說:
如果分為熱資料與冷資料, 建議使用 allkeys-lru 策略。也就是, 其中一部分key經常被讀寫. 如果不確定具體的業務特徵, 那麼 allkeys-lru 是一個很好的選擇。
如果需要循環讀寫所有的key, 或各個key的存取頻率差不多, 可以使用 allkeys-random 策略, 即讀寫所有元素的機率差不多。
假如要讓 Redis 根據 TTL 來篩選需要刪除的key, 請使用 volatile-ttl 策略。
volatile-lru 和 volatile-random 策略主要應用場景是: 既有快取,又有持久key的實例中。一般來說, 像這類場景, 應該使用兩個單獨的 Redis 實例。
值得一提的是, 設定 expire 會消耗額外的記憶體, 所以使用 allkeys-lru 策略, 可以更有效率地利用記憶體, 因為這樣就可以不再設定過期時間了。
驅逐的內部實作
可以這樣理解:
客戶端執行一個指令, 導致Redis 中的資料增加,佔用更多記憶體。
Redis 檢查記憶體使用量, 如果超出 maxmemory 限制, 根據策略清除部分 key。
繼續執行下一條指令, 以此類推。
在這個過程中, 記憶體使用量會不斷地達到 limit 值, 然後超過, 然後刪除部分 key, 使用量又下降到 limit 值之下。
如果某個指令導致大量記憶體佔用(例如透過新key儲存一個很大的set), 在一段時間內, 可能記憶體的使用量會明顯超過 maxmemory 限制。
近似LRU演算法
Redis 使用的並不是完全LRU演算法。自動驅逐的 key , 並不一定是最滿足LRU特徵的那個. 而是通過近似LRU算法, 抽取少量的 key 樣本, 然後刪除其中訪問時間最古老的那個key。
驅逐演算法, 從 Redis 3.0 開始得到了巨大的優化, 使用 pool(池子) 來作為候選. 這大大提升了演算法效率, 也更接近於真實的LRU演算法。
在 Redis 的 LRU 演算法中, 可以透過設定樣本(sample)的數量來調優演算法精度。透過以下指令配置:
maxmemory-samples 5
為什麼不使用完全LRU實作? 原因是為了節省記憶體。但 Redis 的行為和LRU基本上是等價的. 下面是 Redis LRU 與完全LRU演算法的一個行為比較圖。
測試過程中, 依序從第一個 key 開始訪問, 所以最前面的 key 才是最佳的驅逐對象。
從圖中可以看到三種類型的點, 構成了三個不同的條帶。
淺灰色部分錶示被驅逐的物件。
灰色部分錶示 「未被驅逐」 的物件。
綠色部分錶示後面加入的物件。
在純粹的LRU演算法實作中, 前半部舊的key被釋放了。而 Redis 的 LRU 演算法只是將時間較長的 key 較大機率地(probabilistically)釋放了。
如你所見, Redis 3.0 中, 5樣本的效果比 Redis 2.8 好得多。當然, Redis 2.8 也不錯,最後訪問的key基本上都還留在內存中. 在 Redis 3.0 中使用 10 樣本時, 已經非常接近純粹的LRU算法了。
注意,LRU只是用來預測將來可能會繼續訪問某個key的一個概率模型. 此外,如果數據訪問的情況符合冪律分佈(power law), 那麼對於大部分的請求來說, LRU都會表現良好。
在模擬中, 我們發現, 如果使用冪律方式存取, 純粹的LRU和Redis的結果差異非常, 甚至看不出來。
當然也可以將樣本數量提高到10, 以額外消耗一些CPU為代價, 使得結果更接近於真實的LRU, 並通過 cache miss 統計信息來判斷差異。
設定樣本大小很容易, 使用指令CONFIG SET maxmemory-samples
更多Redis相關技術文章,請造訪Redis入門教學專欄進行學習!
以上是redis在哪裡配置快取清理策略的詳細內容。更多資訊請關注PHP中文網其他相關文章!