當(dāng)Redis被當(dāng)做緩存來(lái)使用,當(dāng)你新增數(shù)據(jù)時(shí),讓它自動(dòng)地回收舊數(shù)據(jù)是件很方便的事情。這個(gè)行為在開(kāi)發(fā)者社區(qū)非常有名,因?yàn)樗橇餍械膍emcached系統(tǒng)的默認(rèn)行為。
LRU是Redis唯一支持的回收方法。本頁(yè)面包括一些常規(guī)話題,Redis的maxmemory指令用于將可用內(nèi)存限制成一個(gè)固定大小,還包括了Redis使用的LRU算法,這個(gè)實(shí)際上只是近似的LRU。
Maxmemory配置指令
maxmemory配置指令用于配置Redis存儲(chǔ)數(shù)據(jù)時(shí)指定限制的內(nèi)存大小。通過(guò)redis.conf可以設(shè)置該指令,或者之后使用CONFIG SET命令來(lái)進(jìn)行運(yùn)行時(shí)配置。
例如為了配置內(nèi)存限制為100mb,以下的指令可以放在redis.conf文件中。
maxmemory 100mb
設(shè)置maxmemory為0代表沒(méi)有內(nèi)存限制。對(duì)于64位的系統(tǒng)這是個(gè)默認(rèn)值,對(duì)于32位的系統(tǒng)默認(rèn)內(nèi)存限制為3GB。
當(dāng)指定的內(nèi)存限制大小達(dá)到時(shí),需要選擇不同的行為,也就是策略。 Redis可以?xún)H僅對(duì)命令返回錯(cuò)誤,這將使得內(nèi)存被使用得更多,或者回收一些舊的數(shù)據(jù)來(lái)使得添加數(shù)據(jù)時(shí)可以避免內(nèi)存限制。
回收策略
當(dāng)maxmemory限制達(dá)到的時(shí)候Redis會(huì)使用的行為由 Redis的maxmemory-policy配置指令來(lái)進(jìn)行配置。
以下的策略是可用的:
*noeviction:返回錯(cuò)誤當(dāng)內(nèi)存限制達(dá)到并且客戶端嘗試執(zhí)行會(huì)讓更多內(nèi)存被使用的命令(大部分的寫(xiě)入指令,但DEL和幾個(gè)例外)
-
allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數(shù)據(jù)有空間存放。 -
volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限于在過(guò)期集合的鍵,使得新添加的數(shù)據(jù)有空間存放。 -
allkeys-random: 回收隨機(jī)的鍵使得新添加的數(shù)據(jù)有空間存放。 -
volatile-random: 回收隨機(jī)的鍵使得新添加的數(shù)據(jù)有空間存放,但僅限于在過(guò)期集合的鍵。 -
volatile-ttl: 回收在過(guò)期集合的鍵,并且優(yōu)先回收存活時(shí)間(TTL)較短的鍵,使得新添加的數(shù)據(jù)有空間存放。
如果沒(méi)有鍵滿足回收的前提條件的話,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。
選擇正確的回收策略是非常重要的,這取決于你的應(yīng)用的訪問(wèn)模式,不過(guò)你可以在運(yùn)行時(shí)進(jìn)行相關(guān)的策略調(diào)整,并且監(jiān)控緩存命中率和沒(méi)命中的次數(shù),通過(guò)RedisINFO命令輸出以便調(diào)優(yōu)。
一般的經(jīng)驗(yàn)規(guī)則:
-
使用allkeys-lru策略:當(dāng)你希望你的請(qǐng)求符合一個(gè)冪定律分布,也就是說(shuō),你希望部分的子集元素將比其它其它元素被訪問(wèn)的更多。如果你不確定選擇什么,這是個(gè)很好的選擇。. -
使用allkeys-random:如果你是循環(huán)訪問(wèn),所有的鍵被連續(xù)的掃描,或者你希望請(qǐng)求分布正常(所有元素被訪問(wèn)的概率都差不多)。 -
使用volatile-ttl:如果你想要通過(guò)創(chuàng)建緩存對(duì)象時(shí)設(shè)置TTL值,來(lái)決定哪些對(duì)象應(yīng)該被過(guò)期。
allkeys-lru 和 volatile-random策略對(duì)于當(dāng)你想要單一的實(shí)例實(shí)現(xiàn)緩存及持久化一些鍵時(shí)很有用。不過(guò)一般運(yùn)行兩個(gè)實(shí)例是解決這個(gè)問(wèn)題的更好方法。
為了鍵設(shè)置過(guò)期時(shí)間也是需要消耗內(nèi)存的,所以使用allkeys-lru這種策略更加高效,因?yàn)闆](méi)有必要為鍵取設(shè)置過(guò)期時(shí)間當(dāng)內(nèi)存有壓力時(shí)。
回收進(jìn)程如何工作
理解回收進(jìn)程如何工作是非常重要的:
- 一個(gè)客戶端運(yùn)行了新的命令,添加了新的數(shù)據(jù)。
- Redis檢查內(nèi)存使用情況,如果大于maxmemory的限制, 則根據(jù)設(shè)定好的策略進(jìn)行回收。
- 一個(gè)新的命令被執(zhí)行,等等。
- 不斷地穿越內(nèi)存限制的邊界,通過(guò)不斷達(dá)到邊界然后不斷地回收回到邊界以下。
近似LRU算法
Redis的LRU算法并非完整的實(shí)現(xiàn)。這意味著Redis并沒(méi)辦法選擇最佳候選來(lái)進(jìn)行回收,也就是最久未被訪問(wèn)的鍵。相反它會(huì)嘗試運(yùn)行一個(gè)近似LRU的算法,通過(guò)對(duì)少量keys進(jìn)行取樣,然后回收其中一個(gè)最好的key(被訪問(wèn)時(shí)間較早的)。
不過(guò)從Redis 3.0算法已經(jīng)改進(jìn)為回收鍵的候選池子。這改善了算法的性能,使得更加近似真是的LRU算法的行為。
Redis LRU有個(gè)很重要的點(diǎn),你通過(guò)調(diào)整每次回收時(shí)檢查的采樣數(shù)量,以實(shí)現(xiàn)調(diào)整算法的精度。這個(gè)參數(shù)可以通過(guò)以下的配置指令調(diào)整:
maxmemory-samples 5
Redis為什么不使用真實(shí)的LRU實(shí)現(xiàn)是因?yàn)檫@需要太多的內(nèi)存。不過(guò)近似的LRU算法對(duì)于應(yīng)用而言應(yīng)該是等價(jià)的。使用真實(shí)的LRU算法與近似的算法可以通過(guò)下面的圖像對(duì)比。

用于生成圖像的Redis服務(wù)被填充了指定數(shù)量的鍵。這些鍵將被從頭到尾訪問(wèn),所以當(dāng)使用LRU算法時(shí)第一個(gè)鍵是最佳的回收候選鍵。接著添加超過(guò)50%的鍵,用于強(qiáng)制舊鍵被回收。
你可以看到三種點(diǎn)在圖片中, 形成了三種帶.
- 淺灰色帶是已經(jīng)被回收的對(duì)象。
- 灰色帶是沒(méi)有被回收的對(duì)象。
- 綠色帶是被添加的對(duì)象。
- 在LRU實(shí)現(xiàn)的理論中,我們希望的是,在舊鍵中的第一半將會(huì)過(guò)期。Redis的LRU算法則是概率的過(guò)期舊的鍵。
你可以看到,在都是五個(gè)采樣的時(shí)候Redis 3.0比Redis 2.8要好,Redis2.8中在最后一次訪問(wèn)之間的大多數(shù)的對(duì)象依然保留著。使用10個(gè)采樣大小的Redis 3.0的近似值已經(jīng)非常接近理論的性能。
注意LRU只是個(gè)預(yù)測(cè)鍵將如何被訪問(wèn)的模型。另外,如果你的數(shù)據(jù)訪問(wèn)模式非常接近冪定律,大部分的訪問(wèn)將集中在一個(gè)鍵的集合中,LRU的近似算法將處理得很好。
在模擬實(shí)驗(yàn)的過(guò)程中,我們發(fā)現(xiàn)如果使用冪定律的訪問(wèn)模式,則真實(shí)的LRU算法和近似的Redis算法幾乎沒(méi)有差別。
當(dāng)然你可以提升采樣大小到10,消耗更多的CPU時(shí)間以實(shí)現(xiàn)更真實(shí)的LRU算法,同時(shí)查看下是否讓你的緩存命中率有差別。
通過(guò)CONFIG SET maxmemory-samples命令在生產(chǎn)環(huán)境上設(shè)置不同的采樣大小是非常簡(jiǎn)單的。