鍍金池/ 教程/ 大數(shù)據(jù)/ 使用 Redis 作為 LRU 緩存
使用 Redis 實(shí)現(xiàn) Twitter(上)
集群(下)
使用 Redis 實(shí)現(xiàn) Twitter(下)
使用 Redis 作為 LRU 緩存
高可用(上)
高可用客戶端指引
集群(中)
高可用(下)
持久化
Redis 介紹
集中插入
集群(上)
從入門到精通(上)
從入門到精通(下)
從入門到精通(中)
分片
數(shù)據(jù)類型初探
復(fù)制

使用 Redis 作為 LRU 緩存

當(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 配置指令(configuration directive)

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)存限制。

回收策略(Eviction policies)

當(dāng) maxmemory 限制到達(dá)的時(shí)候,Redis 將采取的準(zhǔn)確行為是由 maxmemory-policy 配置指令配置的。

以下策略可用:

  • noeviction:當(dāng)?shù)竭_(dá)內(nèi)存限制時(shí)返回錯(cuò)誤。當(dāng)客戶端嘗試執(zhí)行命令時(shí)會(huì)導(dǎo)致更多內(nèi)存占用(大多數(shù)寫命令,除了 DEL 和一些例外)。
  • allkeys-lru:回收最近最少使用(LRU)的鍵,為新數(shù)據(jù)騰出空間。
  • volatile-lru:回收最近最少使用(LRU)的鍵,但是只回收有設(shè)置過(guò)期的鍵,為新數(shù)據(jù)騰出空間。
  • allkeys-random:回收隨機(jī)的鍵,為新數(shù)據(jù)騰出空間。
  • volatile-random:回收隨機(jī)的鍵,但是只回收有設(shè)置過(guò)期的鍵,為新數(shù)據(jù)騰出空間。
  • volatile-ttl:回收有設(shè)置過(guò)期的鍵,嘗試先回收離 TTL 最短時(shí)間的鍵,為新數(shù)據(jù)騰出空間。

當(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ī)則:

  • M 如果你期待你的用戶請(qǐng)求呈現(xiàn)冪律分布(power-law distribution),也就是,你期待一部分子集元素被訪問(wèn)得遠(yuǎn)比其他元素多,可以使用 allkeys-lru 策略。在你不確定時(shí)這是一個(gè)好的選擇。
  • 如果你是循環(huán)周期的訪問(wèn),所有的鍵被連續(xù)掃描,或者你期待請(qǐng)求正常分布(每個(gè)元素以相同的概率被訪問(wèn)),可以使用 allkeys-random 策略。
  • 如果你想能給 Redis 提供建議,通過(guò)使用你創(chuàng)建緩存對(duì)象的時(shí)候設(shè)置的 TTL 值,確定哪些對(duì)象應(yīng)該被過(guò)期,你可以使用 volatile-ttl 策略。

當(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ò)程 (Eviction process)

理解回收的過(guò)程是這么運(yùn)作的非常的重要:

  • 一個(gè)客戶端運(yùn)行一個(gè)新命令,添加了新數(shù)據(jù)。
  • Redis 檢查內(nèi)存使用情況,如果大于 maxmemory 限制,根據(jù)策略來(lái)回收鍵。
  • 一個(gè)新的命令被執(zhí)行,如此等等。

我們通過(guò)檢查,然后回收鍵以返回到限制以下,來(lái)連續(xù)不斷的穿越內(nèi)存限制的邊界。

如果一個(gè)命令導(dǎo)致大量的內(nèi)存被占用 (像一個(gè)很大的集合交集保存到一個(gè)新的鍵),一會(huì)功夫內(nèi)存限制就會(huì)被這個(gè)明顯的內(nèi)存量所超越。

近似的 LRU 算法(Approximated LRU algorithm)

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è)不同的帶。

  • 淺灰色帶是被回收的對(duì)象
  • 灰色帶是沒(méi)有被回收的對(duì)象
  • 綠色帶是被添加的對(duì)象

在理論的 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 命令在生產(chǎn)環(huán)境上試驗(yàn)各種不同的采樣大小值是很簡(jiǎn)單的。