1. 程式人生 > 其它 >Redis 記憶體淘汰策略

Redis 記憶體淘汰策略

實際上redis定義了【八種記憶體淘汰策略】來處理redis記憶體滿的情況

  • noeviction:直接返回錯誤,不淘汰任何已經存在的redis鍵
  • allkeys-lru:所有的鍵使用lru演算法進行淘汰
  • volatile-lru:有過期時間的使用lru演算法進行淘汰
  • allkeys-random:隨機刪除redis鍵
  • volatile-random:隨機刪除有過期時間的redis鍵
  • volatile-ttl:刪除快過期的redis鍵
  • volatile-lfu:根據lfu演算法從有過期時間的鍵刪除
  • allkeys-lfu:根據lfu演算法從所有鍵刪除

設定多大的快取容量合適?

具體結合應用資料實際訪問特點和成本開銷來綜合考慮。一般建議把快取容量設定為總資料量的 15% 到 30%,兼顧訪問效能和記憶體空間開銷。

對於 Redis 來說,一旦確定了快取最大容量,比如 4GB,就可以執行下面命令:

CONFIG SET maxmemory 4gb
  • 1

不過,快取被寫滿是不可避免的。

快取替換需要解決兩個問題:

  • 決定淘汰哪些資料
  • 如何處理那些被淘汰的資料

Redis 快取有哪些淘汰策略?

Redis 4.0 之前一共實現了 6 種記憶體淘汰策略,在 4.0 之後,又增加了 2 種策略。我們可以按照是否會進行資料淘汰把它們分成兩類

  • 不進行資料淘汰的策略,只有 noeviction 這一種。
  • 會進行淘汰的 7 種其他策略。

會進行淘汰的 7 種策略,我們可以再進一步根據淘汰候選資料集的範圍把它們分成兩類:

  • 在設定了過期時間的資料中進行淘汰,包括 volatile-random、volatile-ttl、volatilelru、volatile-lfu(Redis 4.0 後新增)四種。
  • 在所有資料範圍內進行淘汰,包括 allkeys-lru、allkeys-random、allkeys-lfu(Redis 4.0 後新增)三種。

(1)預設情況下,redis在使用的記憶體空間超過maxmemory值時,並不會淘汰資料,也就是設定的noeviction 策略。對應到redis快取,也就是值,一旦快取被寫滿了,再有寫請求到來時,redis不會提供服務,而是直接返回錯誤。(因此不推薦使用)

(2)對於 volatile-random、volatile-ttl、volatile-lru 和 volatile-lfu這四種淘汰策略,它們篩選的候選資料範圍,被限制在已經設定了過期時間的鍵值對上。也正是因為如此,即使快取沒有寫滿,這些資料如果過期了,也會被刪除。

  • volatile-ttl 在篩選時,會針對設定了過期時間的鍵值對,根據過期時間的先後進行刪除,越早過期的越先被刪除。
  • volatile-random 就像它的名稱一樣,在設定了過期時間的鍵值對中,進行隨機刪除。
  • volatile-lru 會使用 LRU 演算法篩選設定了過期時間的鍵值對。
  • volatile-lfu 會使用 LFU 演算法選擇設定了過期時間的鍵值對。(LFU演算法會在LRU演算法的基礎上,同時考慮資料的訪問時效性和資料的訪問次數)

(3)對於allkeys-lru、allkeys-random、allkeys-lfu這三種淘汰策略的備選淘汰資料範圍,擴大到了所有鍵值對,無論這些鍵值對是否設定了過期時間。

  • allkeys-random 策略,從所有鍵值對中隨機選擇並刪除資料;
  • allkeys-lru 策略,使用 LRU 演算法在所有資料中進行篩選。
  • allkeys-lfu 策略,使用 LFU 演算法在所有資料中進行篩選

也就是說,如果一個鍵值對被刪除策略選中了,即使它的過期時間還沒到,也需要被刪除。當然,如果它的過期時間到了但未被策略選中,同樣也會被刪除。

怎麼使用呢?

  • 優先使用allkeys-lru策略。這樣,可以充分利用LRU演算法的優勢,把最近最常訪問的資料留在快取中,提升應用的訪問效能。如果你的業務資料中有明顯的冷熱資料的區分,建議使用allkeys-lru策略
  • 如果業務應用中的資料訪問頻率不大,沒有明顯的冷熱資料區分,建議使用allkeys-random策略,隨機淘汰資料即可
  • 如果業務有置頂的需求,比如置頂新聞、置頂視訊,那麼,可以使用 volatile-lru策略,同時不給這些置頂資料設定過期時間。這樣一來,這些需要置頂的資料一直不會被刪除,而其他資料會在過期時根據 LRU 規則進行篩選

LRU演算法

LRU 演算法的全稱是 Least Recently Used,從名字上就可以看出,這是按照最近最少使用的原則來篩選資料,最不常用的資料會被篩選出來,而最近頻繁使用的資料會留在快取中。

那具體是怎麼篩選的呢? LRU會把所有的資料組織成一個連結串列,連結串列的頭和尾分別表示MRU端和LRU段,分別代表最近最常使用的資料和最近最不常用的資料。

如下圖

  • 我們現在有資料 6、3、9、20、5。如果資料 20 和 3 被先後訪問,它們都會從現有的連結串列位置移到 MRU 端,而連結串列中在它們之前的資料則相應地往後移一位。因為,LRU 演算法選擇刪除資料時,都是從 LRU 端開始,所以把剛剛被訪問的資料移到 MRU 端,就可以讓它們儘可能地留在快取中。
  • 如果有一個新資料 15 要被寫入快取,但此時已經沒有快取空間了,也就是連結串列沒有空餘位置了,那麼,LRU 演算法做兩件事:
    • 資料 15 是剛被訪問的,所以它會被放到 MRU 端;
    • 演算法把 LRU 端的資料 5 從快取中刪除,相應的連結串列中就沒有資料 5 的記錄了。

其實,LRU 演算法背後的想法非常樸素:它認為剛剛被訪問的資料,肯定還會被再次訪問,所以就把它放在 MRU 端;長久不訪問的資料,肯定就不會再被訪問了,所以就讓它逐漸後移到 LRU 端,在快取滿時,就優先刪除它。

不過,LRU演算法在實際實現時,需要用連結串列管理所有的快取資料,這會帶來額外的空間開銷。而且,當有資料被訪問時,需要在連結串列上把該資料移動到MRU端,如果有大量資料被訪問,就會帶來很多連結串列移動操作,會很耗時,進而降低redis快取效能。

所以,在redis中,LRU演算法被做了簡化,以減輕淘汰資料對快取效能的影響。

  • 具體來說,redis會預設記錄每一個數據的最近一次訪問的時間戳(由鍵值對資料結構RedisObject 中的 lru 欄位記錄)。
  • 然後,redis在決定淘汰的資料時,第一次會隨機選出N個數據,把它們作為一個候選集合。
  • 接下來,redis會比較這N個數據的lru欄位,把lru欄位值最小的資料從快取中淘汰出去。

Redis 提供了一個配置引數 maxmemory-samples,這個引數就是 Redis 選出的資料個數N。

  • 例如,我們執行如下命令,可以讓 Redis 選出 100 個數據作為候選資料集:
 CONFIG SET maxmemory-samples 100
  • 1
  • 當再次淘汰資料時,redis需要挑選資料進入第一次淘汰時建立的候選集合。這裡的挑選標準是:能進入候選集合的資料的lru欄位值小於候選集合中最小的 lru 值。
  • 當有新資料進入候選集合後,如果候選資料集合中的資料個數達到了maxmemory-samples,redis就把候選集合集中lru子彈值最下的資料淘汰出去。

這樣一來,Redis 快取不用為所有的資料維護一個大連結串列,也不用在每次資料訪問時都移動連結串列項,提升了快取的效能。

一旦被淘汰的資料被選定後,Redis 怎麼處理這些資料呢?

如何處理被淘汰的資料?

一般來說,一旦被淘汰的資料選定後,如果這個資料時乾淨資料,那我們就直接刪除;如果這個資料是髒資料,我們需要把它寫回資料庫


那怎麼判斷一個數據到底是乾淨的還是髒的呢?

  • 乾淨資料和髒資料的區別就在於,和最初從後端資料庫裡讀取時的值相比,有沒有被修改過。乾淨資料一直沒有被修改,所以後端資料庫裡的資料也是最新值。在替換時,它可以被直接刪除。
  • 而髒資料就是曾經被修改過的,已經和後端資料庫中儲存的資料不一致了。此時,如果不把髒資料寫回到資料庫中,這個資料的最新值就丟失了,就會影響應用的正常使用。

不過,對於redis來說,它決定了被淘汰的資料後,會把它們刪除。即使淘汰的資料時髒資料,redis也不會把它們寫回資料庫。所以,我們在使用redis快取時,如果資料被修改了,需要在資料修改時就將它寫回資料庫。否則,這個髒資料被淘汰時,會被redis刪除,而資料庫裡也沒有最新的資料了。

小結

  • Redis 4.0 版本以後一共提供了 8 種資料淘汰策略,從淘汰資料的候選集範圍來看,我們有兩種候選範圍:一種是所有資料都是候選集,一種是設定了過期時間的資料是候選集。另外,無論是面向哪種候選資料集進行淘汰資料選擇,我們都有三種策略,分別是隨機選擇,根據 LRU 演算法選擇,以及根據 LFU 演算法選擇。當然,當面向設定了過期時間的資料集選擇淘汰資料時,我們還可以根據資料離過期時間的遠近來決定。
  • 一般來說,快取系統對於選定的被淘汰資料,會根據其是乾淨資料還是髒資料,選擇直接刪除還是寫回資料庫。但是,在 Redis 中,被淘汰資料無論乾淨與否都會被刪除,所以,這是我們在使用 Redis 快取時要特別注意的:當資料修改成為髒資料時,需要在資料庫中也把資料修改過來
  • 選擇哪種快取策略是值得我們多加琢磨的,它在篩選資料方面是否能篩選出可能被再次訪問的資料,直接決定了快取效率的高與低。
  • 很簡單的一個對比,如果我們使用隨機策略,剛篩選出來的要被刪除的資料可能正好又被訪問了,此時應用就只能花費幾毫秒從資料庫中讀取資料了。而如果使用 LRU 策略,被篩選出來的資料往往是經過時間驗證了,如果在一段時間內一直沒有訪問,本身被再次訪問的概率也很低了。
  • 所以,建議,先根據是否有始終會被頻繁訪問的資料(例如置頂訊息),來選擇淘汰資料的候選集,也就是決定是針對所有資料進行淘汰,還是針對設定了過期時間的資料進行淘汰。候選資料集範圍選定後,建議優先使用 LRU 演算法,也就是,allkeys-lru 或volatile-lru 策略。
  • 當然,設定快取容量的大小也很重要,我的建議是:結合實際應用的資料總量、熱資料的體量,以及成本預算,把快取空間大小設定在總資料量的 15% 到 30% 這個區間就可以