1. 程式人生 > >用redis當作LRU快取

用redis當作LRU快取

原文地址:https://redis.io/topics/lru-cache

Redis可以用來作快取,他可以很方便的淘汰(刪除)舊資料新增新資料,類似memcached。LRU只是其中的一種置換演算法,這篇文章介紹了maxmemory配置命令和LRU演算法的一些深入討論,這裡的LRU只是一種近似LRU(並不是嚴格的把最老的資料淘汰,而是使用隨機取樣的方式)。從4.0開始Redis引入了一種新的淘汰演算法LFU(Least Frequently Used)。

maxmemory配置命令

maxmemory配置命令用來指定Redis儲存資料的大小,可以在redis.conf裡配置,也可以執行的時候用CONFIG SET配置。比如配置成使用100M記憶體,配置命令如下

maxmemory 100mb

如果把maxmemory設定成0就是不對記憶體使用量做限制。在64位系統上預設是設定成0,在32位系統上預設設定成3GB。如果達到了限制記憶體,會根據配置採取不同的策略,可以給相應的命令返回錯誤,也可以淘汰舊資料來存入新資料。

淘汰策略

在配置檔案裡使用maxmemory-policy命令指定淘汰策略,取值如下

  • noeviction:達到限制記憶體以後再存新資料會返回出錯。
  • allkeys-lru:淘汰最近沒使用的資料。
  • volatile-lru:在設定了過期值(expire)的資料裡淘汰最近沒使用的資料。
  • allkeys-random:隨機淘汰資料。
  • volatile-random:在設定了過期值(expire)的資料裡隨機淘汰資料。
  • volatile-ttl:在設定了過期值(expire)的資料裡淘汰快要過期的資料。

volatile-lru,volatile-random和volatile-ttl在沒有可淘汰的資料的時候也會像noeviction一樣返回出錯。怎樣選擇淘汰策略取決於你的程式,也可以在使用的過程中用INFO命令觀察命中率再做調整。一般情況按如下方法選擇淘汰策略:

  • 如果所儲存的資料訪問量成冪律分佈的,也就是說一部分資料的訪問量明顯多於其他資料,那麼就使用allkeys-lru。
  • 如果所儲存的資料週期訪問的,或者被訪問的概率大致相同,那麼就是用allkeys-random。
  • 如果所儲存的資料設定了不同的過期時間,可以用volatile-ttl。

如果在一個redis例項裡儲存了兩種資料(永久資料和帶過期時間的資料),使用volatile-lru和volatile-random是比較好的選擇。當然更好的辦法是把這兩種資料分別存在一個redis例項裡。給資料設定過期時間是需要消耗記憶體的,所有使用allkeys-lru會更節省記憶體。

淘汰資料的過程

  • 客戶端請求新增資料。
  • redis檢查記憶體是否超過了maxmemory限制,如果超過了就根據策略淘汰舊資料。
  • 新增客戶端要求的新增的資料。

所有記憶體使用量會一直在maxmemory上下徘徊,如果某個命令要求新增一個很大的資料很可能造成redis使用的記憶體明顯超過了maxmemory限制。

近似LRU演算法

redis的LRU沒有嚴格的實現LRU,也就是說redis不是淘汰最佳(最久沒訪問)的資料。redis會做一個隨機取樣,淘汰樣本里最佳資料。3.0以後redis會使用備選池做淘汰,提升了效能,準確性更高,更接近LRU演算法(程式碼裡提升效能很明顯,準確率更高這點看不出來,可能官方做統計得出的結論)。可以設定樣本大小調整演算法的準確率。

maxmemory-samples 5

redis沒用真正的LRU實現是因為這正的LRU太消耗記憶體了,要遍歷排序,會慢很多。下圖是近似LRU演算法和真正的LRU演算法的測試對比圖

測試的時候先生成一些資料。從第一個資料開始儲存,一直到最後一個,所以第一個資料是LRU演算法的最佳淘汰物件。最後又新增50%的資料用來淘汰舊資料。上面的三個色帶分別是:

  • 淺灰色了淘汰的資料
  • 灰色是沒有淘汰的資料
  • 綠色是新增的資料。

理論上最先新增的一半資料應該淘汰,redis的LRU演算法只是概率性的淘汰這些舊資料。可以看出來在maxmemory-samples設定成5的時候3.0版本比2.8版本更精確(相對來說更接近理論值)。maxmemory-samples設定成10的時候3.0版本已經相對接近理論值了。

LRU只是預測將來一段時間的資料訪問模型,如果你的資料成冪律分佈redis的LRU也能非常好的處理這個模型。 redis的LRU和真正的LRU差距非常小甚至沒差距。如果想要更接近LRU可以提高樣本採集量把maxmemory-samples設定成10。

新的近似LFU模式

從4.0開始redis引入了一個新的淘汰模型LFU(Least Frequently Used)。一些情況下這個模型在命中率上更會準確。LFU會統計資料的使用率,使用率低的會被淘汰,使用率高的留下。LRU會保留最近訪問了但是平常訪問率很低的資料,風險就是淘汰了一個平常訪問率高但是最近沒訪問的資料。LFU不會存在這樣的問題,所以LFU更適用於各種不同的訪問模型。LFU策略配置如下:

  • volatile-lfu:在設定了過期時間的資料裡使用近似LFU淘汰演算法。
  • allkeys-lfu:在所有的資料上使用LFU淘汰演算法。

LFU和LRU類似也是一種概率計算,LFU使用一個叫morris counter的概率統計方法,一個數據只使用幾個位元。和一個叫過期時間(decay period)的數結合使用。統計值(counter)隨著時間減小。LFU也是使用取樣的方式淘汰資料。LFU有更多的調整選項,4.0預設的配置

  • 一百萬訪問量會使統計值(counter)變為最大
  • 衰敗期是一分鐘

這些是經過測試比較好用的,使用者也可以自己設定。配置命令如下:

lfu-log-factor 10
lfu-decay-time 1

一個比較特殊的用法是衰敗期設定成0,每次統計都會衰敗,這是個比較少用的方法。指數因子用來指定多大的訪問量會是統計值變為最大,統計值範圍0-255。指數因子越大需要越多的訪問量使統計值變為最大,指數因子越小訪問量低的時候解析度越高。

+--------+------------+------------+------------+------------+------------+
| factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |
+--------+------------+------------+------------+------------+------------+
| 0      | 104        | 255        | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 1      | 18         | 49         | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 10     | 10         | 18         | 142        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 100    | 8          | 11         | 49         | 143        | 255        |
+--------+------------+------------+------------+------------+------------+

所以指數因子需要在解析度和高訪問之間做一個折中。redis.conf裡有更詳細的說明。