08 | 替换策略: 缓存满了怎么办?
1. 前言
??為了保證較高的性價比,緩存的空間容量必然要小于后端數據庫的數據總量。不過,內存大小畢竟有限,隨著要緩存的數據量越來越大,有限的緩存空間不可避免地會被寫滿。此時,該怎么辦呢?
??解決這個問題就涉及到緩存系統(tǒng)的一個重要機制,即緩存數據的淘汰機制。簡單來說,數據淘汰機制包括兩步:第一,根據一定的策略,篩選出對應用訪問來說“不重要”的數據;第二,將這些數據從緩存中刪除,為新來的數據騰出空間,
2. 設置的緩存容量
??我們先看看藍線。它表示的就是“八二原理”,有 20% 的數據貢獻了 80% 的訪問了,而剩余的數據雖然體量很大,但只貢獻了 20% 的訪問量。這 80% 的數據在訪問量上就形成了一條長長的尾巴,我們也稱為“長尾效應”。我會建議把緩存容量設置為總數據量的 15% 到 30%,兼顧訪問性能和內存空間開銷。
3. Redis緩存有淘汰策略
??我們可以按照是否會進行數據淘汰把它們分成兩類:
- 不進行數據淘汰的策略,只有 noeviction 這一種。
- 會進行淘汰的 7 種其他策略。
會進行淘汰的 7 種策略,
??我們可以再進一步根據淘汰候選數據集的范圍把它們分成兩類:
- 在設置了過期時間的數據中進行淘汰,包括 volatile-random、volatile-ttl、volatile、lru、volatile-lfu(Redis 4.0 后新增)四種。
- 在所有數據范圍內進行淘汰,包括 allkeys-lru、allkeys-random、allkeys-lfu(Redis4.0 后新增)三種。
??默認情況下,Redis 在使用的內存空間超過 maxmemory 值時,并不會淘汰數據,也就是設定的noeviction 策略。對應到 Redis 緩存,也就是指,一旦緩存被寫滿了,再有寫請求來時,Redis 不再提供服務,而是直接返回錯誤。Redis 用作緩存時,實際的數據集通常都是大于緩存容量的,總會有新的數據要寫入緩存,這個策略本身不淘汰數據,也就不會騰出新的緩存空間,我們不把它用在 Redis 緩存中。
??我們再分析下 volatile-random、volatile-ttl、volatile-lru 和 volatile-lfu 這四種淘汰策略。它們篩選的候選數據范圍,被限制在已經設置了過期時間的鍵值對上。也正因為此,即使緩存沒有寫滿,這些數據如果過期了,也會被刪除。
??例如,我們使用 EXPIRE 命令對一批鍵值對設置了過期時間后,無論是這些鍵值對的過期時間是快到了,還是 Redis 的內存使用量達到了 maxmemory 閾值,Redis 都會進一步按照 volatile-ttl、volatile-random、volatile-lru、volatile-lfu 這四種策略的具體篩選規(guī)則進行淘汰。
- volatile-ttl 在篩選時,會針對設置了過期時間的鍵值對,根據過期時間的先后進行刪除,越早過期的越先被刪除。
- volatile-random 就像它的名稱一樣,在設置了過期時間的鍵值對中,進行隨機刪除。
- volatile-lru 會使用 LRU 算法篩選設置了過期時間的鍵值對。
- volatile-lfu 會使用 LFU 算法選擇設置了過期時間的鍵值對
??相對于 volatile-ttl、volatile-random、volatile-lru、volatile-lfu 這四種策略淘汰的是設置了過期時間的數據,allkeys-lru、allkeys-random、allkeys-lfu 這三種淘汰策略的備選淘汰數據范圍,就擴大到了所有鍵值對,無論這些鍵值對是否設置了過期時間。它們篩選數據進行淘汰的規(guī)則是:
- allkeys-random 策略,從所有鍵值對中隨機選擇并刪除數據;
- allkeys-lru 策略,使用 LRU 算法在所有數據中進行篩選。
- allkeys-lfu 策略,使用 LFU 算法在所有數據中進行篩選。
??LRU 算法的全稱是 Least Recently Used,從名字上就可以看出,這是按照最近最少使用的原則來篩選數據,最不常用的數據會被篩選出來,而最近頻繁使用的數據會留在緩存中。
??那具體是怎么篩選的呢?LRU 會把所有的數據組織成一個鏈表,鏈表的頭和尾分別表示MRU 端和 LRU 端,分別代表最近最常使用的數據和最近最不常用的數據。我們看一個例子。
??LRU 算法在實際實現時,需要用鏈表管理所有的緩存數據,這會帶來額外的空間開銷。而且,當有數據被訪問時,需要在鏈表上把該數據移動到 MRU 端,如果有大量數據被訪問,就會帶來很多鏈表移動操作,會很耗時,進而會降低 Redis 緩存性能。
??所以,在 Redis 中,LRU 算法被做了簡化,以減輕數據淘汰對緩存性能的影響。具體來說,Redis 默認會記錄每個數據的最近一次訪問的時間戳(由鍵值對數據結構RedisObject 中的 lru 字段記錄)。然后,Redis 在決定淘汰的數據時,第一次會隨機選出N 個數據,把它們作為一個候選集合。接下來,Redis 會比較這 N 個數據的 lru 字段,把lru 字段值最小的數據從緩存中淘汰出去。
??Redis 提供了一個配置參數 maxmemory-samples,這個參數就是 Redis 選出的數據個數N。例如,我們執(zhí)行如下命令,可以讓 Redis 選出 100 個數據作為候選數據集:
??當需要再次淘汰數據時,Redis 需要挑選數據進入第一次淘汰時創(chuàng)建的候選集合。這兒的挑選標準是:能進入候選集合的數據的 lru 字段值必須小于候選集合中最小的 lru 值。有新數據進入候選數據集后,如果候選數據集中的數據個數達到了 maxmemory-samples,Redis 就把候選數據集中 lru 字段值最小的數據淘汰出去。
??我們就學完了除了使用 LFU 算法以外的 5 種緩存淘汰策略,我再給你三個使用建議。
- 優(yōu)先使用 allkeys-lru 策略。這樣,可以充分利用 LRU 這一經典緩存算法的優(yōu)勢,把最近最常訪問的數據留在緩存中,提升應用的訪問性能。
- 如果你的業(yè)務數據中有明顯的冷熱數據區(qū)分,我建議你使用 allkeys-lru 策略。
- 如果業(yè)務應用中的數據訪問頻率相差不大,沒有明顯的冷熱數據區(qū)分,建議使用allkeys-random 策略,隨機選擇淘汰的數據就行。
- 如果你的業(yè)務中有置頂的需求,比如置頂新聞、置頂視頻,那么,可以使用 volatile-lru策略,同時不給這些置頂數據設置過期時間。這樣一來,這些需要置頂的數據一直不會被刪除,而其他數據會在過期時根據 LRU 規(guī)則進行篩選。
總結
以上是生活随笔為你收集整理的08 | 替换策略: 缓存满了怎么办?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 07丨切片集群:数据增多了,是该加内存还
- 下一篇: 09丨缓存异常:如何解决缓存和数据库的数