當(dāng) Redis 作為緩存使用時(shí),當(dāng)你添加新的數(shù)據(jù)時(shí),有時(shí)候很方便使 Redis 自動(dòng)回收老的數(shù)據(jù)。這種行為在開(kāi)發(fā)者社區(qū)中眾所周知,因?yàn)檫@是流行的 memcached 系統(tǒng)的默認(rèn)行為。
LRU 實(shí)際上是被唯一支持的數(shù)據(jù)移除方法。本文內(nèi)容將包含 Redis 的 maxmemory 指令,用于限制內(nèi)存使用到一個(gè)固定的容量,也包含深入探討 Redis 使用的 LRU 算法,一個(gè)近似準(zhǔn)確的 LRU。
maxmemory 配置指令是用來(lái)配置 Redis 為數(shù)據(jù)集使用指定的內(nèi)存容量大小??梢允褂?redis.conf 文件來(lái)設(shè)置配置指令,或者之后在運(yùn)行時(shí)使用 CONFIG SET 命令。
例如,為了配置內(nèi)存限制為 100MB,可以在 redis.conf 文件中使用以下指令
maxmemory 100mb
設(shè)置 maxmemory 為 0,表示沒(méi)有內(nèi)存限制。這是 64 位系統(tǒng)的默認(rèn)行為,32 位的系統(tǒng)則使用 3G 大小作為隱式的內(nèi)存限制。
當(dāng)指定的內(nèi)存容量到達(dá)時(shí),需要選擇不同的行為,即策略。Redis 可以只為命令返回錯(cuò)誤,這樣將占用更多的內(nèi)存,或者每次添加新數(shù)據(jù)時(shí),回收掉一些舊的數(shù)據(jù)以避免內(nèi)存限制。
當(dāng) maxmemory 限制到達(dá)的時(shí)候,Redis 將采取的準(zhǔn)確行為是由 maxmemory-policy 配置指令配置的。
以下策略可用:
當(dāng)沒(méi)有滿足前提條件的話,volatile-lru,volatile-random 和 volatile-ttl 策略就表現(xiàn)得和 noeviction 一樣了。
選擇正確的回收策略是很重要的,取決于你的應(yīng)用程序的訪問(wèn)模式,但是,你可以在程序運(yùn)行時(shí)重新配置策略,使用 INFO 輸出來(lái)監(jiān)控緩存命中和錯(cuò)過(guò)的次數(shù),以調(diào)優(yōu)你的設(shè)置。
一般經(jīng)驗(yàn)規(guī)則:
當(dāng)你想使用單個(gè)實(shí)例來(lái)實(shí)現(xiàn)緩存和持久化一些鍵,allkeys-lru 和 volatile-random 策略會(huì)很有用。但是,通常最好是運(yùn)行兩個(gè) Redis 實(shí)例來(lái)解決這個(gè)問(wèn)題。
另外值得注意的是,為鍵設(shè)置過(guò)期時(shí)間需要消耗內(nèi)存,所以使用像 allkeys-lru 這樣的策略會(huì)更高效,因?yàn)樵趦?nèi)存壓力下沒(méi)有必要為鍵的回收設(shè)置過(guò)期時(shí)間。
理解回收的過(guò)程是這么運(yùn)作的非常的重要:
我們通過(guò)檢查,然后回收鍵以返回到限制以下,來(lái)連續(xù)不斷的穿越內(nèi)存限制的邊界。
如果一個(gè)命令導(dǎo)致大量的內(nèi)存被占用 (像一個(gè)很大的集合交集保存到一個(gè)新的鍵),一會(huì)功夫內(nèi)存限制就會(huì)被這個(gè)明顯的內(nèi)存量所超越。
Redis 的 LRU 算法不是一個(gè)精確的實(shí)現(xiàn)。這意味著 Redis 不能選擇最佳候選鍵來(lái)回收,也就是最久錢被訪問(wèn)的那些鍵。相反,會(huì)嘗試運(yùn)營(yíng)一個(gè)近似的 LRU 算法,通過(guò)采樣一小部分鍵,然后在采樣鍵中回收最適合(擁有最久訪問(wèn)時(shí)間)的那個(gè)。
然而,從 Redis3.0(當(dāng)前還是 beta 版本)開(kāi)始,算法被改進(jìn)為持有回收候選鍵的一個(gè)池子。這改善了算法的性能,使得更接近于真實(shí)的 LRU 算法的行為
Redis 的 LRU 算法有一點(diǎn)很重要,你可以調(diào)整算法的精度,通過(guò)改變每次回收時(shí)檢查的采樣數(shù)量。這個(gè)參數(shù)可以通過(guò)如下配置指令:
maxmemory-samples 5
Redis 沒(méi)有使用真實(shí)的 LRU 實(shí)現(xiàn)的原因,是因?yàn)檫@會(huì)消耗更多的內(nèi)存。然而,近似值對(duì)使用 Redis 的應(yīng)用來(lái)說(shuō)基本上也是等價(jià)的。下面的圖形對(duì)比,為 Redis 使用的 LRU 近似值和真實(shí) LRU 之間的比較。
用于測(cè)試生成上面圖像的 Redis 服務(wù)被填充了指定數(shù)量的鍵。鍵被從頭訪問(wèn)到尾,所以第一個(gè)鍵是 LRU 算法的最佳候選回收鍵。然后,再新添加 50% 的鍵,強(qiáng)制一般的舊鍵被回收。
你可以從圖中看到三種不同的原點(diǎn),形成三個(gè)不同的帶。
在理論的 LRU 實(shí)現(xiàn)中,我們期待看到的是,在舊鍵中第一半會(huì)過(guò)期。而 Redis 的 LRU 算法則只是概率性的過(guò)期這些舊鍵。
你可以看到,同樣采用 5 個(gè)采樣,Redis 3.0 表現(xiàn)得比 Redis 2.8 要好,Redis 2.8 中最近被訪問(wèn)的對(duì)象之間的對(duì)象仍然被保留。在 Redis 3.0 中使用 10 為采樣大小,近似值已經(jīng)非常接近理論性能。
注意,LRU 只是一個(gè)預(yù)言指定鍵在未來(lái)如何被訪問(wèn)的模式。另外,如果你的數(shù)據(jù)訪問(wèn)模式非常接近冪律,大多數(shù)的訪問(wèn)都將集中在一個(gè)集合中,LRU 近似算法將能處理得很好。
在模擬實(shí)驗(yàn)的過(guò)程中,我們發(fā)現(xiàn)使用冪律訪問(wèn)模式,真實(shí)的 LRU 算法和 Redis 的近似算法之間的差異非常小,或者根本就沒(méi)有。
然而,你可以提高采樣大小到 10,這會(huì)消耗額外的 CPU,來(lái)更加近似于真實(shí)的 LRU 算法,看看這會(huì)不會(huì)使你的緩存錯(cuò)失率有差異。
使用 CONFIG SET maxmemory-samples