當Redis用做緩存時,一般很方便在添加新數據時讓它自動逐出舊數據。此行爲在開發人員社區中是衆所周知的,由於它是流行的內存緩存系統的默認行爲 。redis
LRU實際上只是支持的驅逐方法之一。本頁涵蓋Redis maxmemory指令的更通常主題,該指令用於將內存使用量限制爲固定數量,而且還深刻介紹了Redis使用的LRU算法,其實是確切的LRU的近似值。算法
從Redis版本4.0開始,引入了新的LFU(最不經常使用)驅逐策略。本文檔的單獨部分對此進行了介紹。緩存
Maxmemory配置指令
使用maxmemory配置指令是爲了將Redis配置爲對數據集使用指定的內存量。可使用redis.conf文件來設置配置指令,或者稍後在運行時使用CONFIG SET命令來設置。服務器
例如,爲了配置100 MB的內存限制,能夠在redis.conf文件內部使用如下指令。dom
maxmemory 100mb
設置maxmemory爲零將致使沒有內存限制。這是64位系統的默認行爲,而32位系統使用3GB的隱式內存限制。性能
當達到指定的內存量時,能夠在不一樣的行爲之間進行選擇,這稱爲策略。Redis只會爲可能致使使用更多內存的命令返回錯誤,或者它能夠逐出某些舊數據,以便在每次添加新數據時返回到指定的限制。測試
驅逐政策
maxmemory使用maxmemory-policy配置指令配置達到限制時,會發生確切的行爲Redis 。對象
可使用如下策略:內存
noeviction:在達到內存限制而且客戶端嘗試執行可能致使使用更多內存的命令時返回錯誤(大多數寫入命令,但DEL和一些其餘異常)。
allkeys-lru:經過嘗試先刪除較新使用的(LRU)鍵來退出鍵,以便爲添加的新數據騰出空間。
volatile-lru:經過嘗試先刪除較新使用的(LRU)密鑰來退出密鑰,但僅在已設置了expire set的密鑰之間,以便爲添加的新數據騰出空間。
allkeys-random:隨機逐出密鑰,以便爲添加的新數據騰出空間。
volatile-random:隨機逐出鍵,以便爲添加的新數據騰出空間,但僅逐出設置了expire set的鍵。
volatile-ttl:逐出設置了expire的鍵,並嘗試首先逐出具備較短生存時間(TTL)的鍵,以便爲添加的新數據騰出空間。
該政策揮發性-LRU,揮發性隨機和揮發性-TTL的行爲很像noeviction若是沒有鑰匙驅逐匹配的先決條件。開發
選擇正確的逐出策略很重要,具體取決於應用程序的訪問模式,可是您能夠在應用程序運行時在運行時從新配置該策略,並使用Redis INFO輸出監視緩存未命中和命中的次數,以調整設置。 。
通常而言,根據經驗:
當您但願請求的流行程度具備冪律分佈時,請使用allkeys-lru策略,也就是說,您但願訪問元素的子集比訪問其餘元素更多。若是不肯定,這是一個不錯的選擇。
若是您具備對全部密鑰進行連續掃描的週期性訪問,或者當您指望分佈是統一的(全部元素以相同的機率被訪問)時,請使用allkeys-random。
若是您但願可以在建立緩存對象時經過使用不一樣的TTL值向Redis提供有關哪些是最佳到期候選者的提示,請使用volatile-ttl。
當您要使用單個實例進行緩存並擁有一組持久密鑰時,volatile-lru和volatile-random策略主要有用。可是,一般最好運行兩個Redis實例來解決此問題。
還值得注意的是,將密鑰設置爲過時會消耗內存,所以使用諸如allkeys-lru之類的策略會提升內存效率,由於無需爲要在內存壓力下驅逐密鑰設置過時。
驅逐過程如何進行
重要的是要了解驅逐過程的工做方式以下:
客戶端運行新命令,從而添加更多數據。
Redis會檢查內存使用狀況,若是大於使用maxmemory限制,則會根據策略逐出密鑰。
執行新命令,依此類推。
所以,咱們不斷越過內存限制,而後越過該限制,而後逐出按鍵以在限制範圍內返回,從而不斷跨越該限制。
若是某個命令致使大量內存被使用(例如,將較大的交集存儲到新鍵中)一段時間,則內存限制可能會超出明顯的數量。
近似LRU算法
Redis LRU算法不是確切的實現。這意味着Redis沒法選擇最好的驅逐對象,即過去訪問最多的訪問。取而代之的是,它將嘗試對LRU算法進行近似處理,方法是對少許密鑰進行採樣,而後從採樣的密鑰中驅出最好的(訪問時間最長)密鑰。
可是,自Redis 3.0起,對該算法進行了改進,使其也能夠將大量優秀候選人逐出。這提升了算法的性能,使其可以更接近地逼真的LRU算法的行爲。
Redis LRU算法的重要意義在於,您能夠經過更改樣本數量來檢查每次逐出,從而調整算法的精度。此參數由如下配置指令控制:
maxmemory-samples 5
Redis之因此不使用真正的LRU實現,是由於它佔用更多內存。可是,近似值實際上與使用Redis的應用程序等效。如下是Redis使用的LRU近似與真實LRU比較的圖形比較。
LRU比較
生成以上圖形的測試用給定數量的密鑰填充了Redis服務器。密鑰是從第一個到最後一個訪問的,所以第一個密鑰是使用LRU算法驅逐的最佳候選者。後來又添加了50%的密鑰,以強制淘汰一半的舊密鑰。
您能夠在圖形中看到三種點,造成三個不一樣的帶。
淺灰色帶是被逐出的對象。
灰帶是未被逐出的對象。
綠帶是添加的對象。
在理論上的LRU實現中,咱們指望在舊密鑰中,前半部分將過時。相反,Redis LRU算法只會機率地使較早的密鑰過時。
如您所見,與Redis 2.8相比,Redis 3.0在5個樣本上作得更好,可是Redis 2.8仍保留了最新訪問的對象中的大多數。在Redis 3.0中使用10的樣本大小,近似值很是接近Redis 3.0的理論性能。
請注意,LRU只是預測將來將訪問給定密鑰的可能性的模型。此外,若是您的數據訪問模式與冪律極爲類似,則大多數訪問將位於LRU近似算法將可以很好處理的密鑰集中。
在仿真中,咱們發現使用冪定律訪問模式,真實LRU和Redis近似之間的差別很小或不存在。
可是,您能夠以一些額外的CPU使用爲代價將樣本大小增長到10,以接近真實的LRU,並檢查這是否會致使高速緩存未命中率有所不一樣。
使用CONFIG SET maxmemory-samples <count>命令以不一樣的樣本量值在生產中進行實驗很是簡單。
新的LFU模式
從Redis 4.0開始,可使用新的「 最少使用」逐出模式。在某些狀況下,此模式可能會更好地工做(提供更好的命中率/未命中率),由於使用LFU Redis會嘗試跟蹤物品的訪問頻率,所以,極少使用的物品會被驅逐,而常用的物品則有更高的機會保留在內存中。
若是您認爲在LRU,最近訪問過但實際上幾乎從未請求過的項目不會過時,所以風險在於逐出未來有更高機會被請求的密鑰。LFU沒有這個問題,一般應該更好地適應不一樣的訪問模式。
要配置LFU模式,可使用如下策略:
volatile-lfu 使用具備過時集的密鑰在近似LFU中進行驅逐。
allkeys-lfu 使用近似的LFU退出任何密鑰。
LFU近似於LRU:它使用機率計數器(稱爲莫里斯計數器),以便僅使用每一個對象幾個位來估計對象訪問頻率,並結合一個衰減週期,以便使計數器隨時間減小:在某些時候,咱們再也不但願將密鑰視爲頻繁訪問的密鑰,即便它們是過去的密鑰也是如此,所以該算法能夠適應訪問模式的轉變。
這些信息的採樣方式與LRU發生的狀況相似(如本文檔前面的部分中所述),以選擇驅逐候選人。
可是,與LRU不一樣,LFU具備某些可調參數:例如,若是頻繁訪問的項目再也不被訪問,應該將其下降多快?還能夠調整Morris計數器範圍,以使算法更好地適應特定的用例。
默認狀況下,Redis 4.0配置爲:
在大約一百萬個請求時使計數器飽和。
每隔一分鐘使計數器衰減一次。
這些應該是合理的值,而且已通過實驗測試,可是用戶可能但願使用這些配置設置來選擇最佳值。
有關如何調整這些參數的說明,能夠redis.conf在源代碼發行版的示例文件中找到,但簡要地說,它們是:
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文件自我記錄註釋中提供了更多信息。
因爲LFU是一項新功能,所以與LRU相比,咱們將很是感謝您提供有關LFU在您的用例中的性能的反饋。