Redis緩存失效策略


一、背景

線上你寫代碼的時候,想當然的認為寫進 redis 的數據就一定會存在,后面導致系統各種 bug,誰來負責?
常見的有兩個問題:
往 redis 寫入的數據怎么沒了?
可能有同學會遇到,在生產環境的 redis 經常會丟掉一些數據,寫進去了,過一會兒可能就沒了。我的天,同學,你問這個問題就說明 redis 你就沒用對啊。redis 是緩存,你給當存儲了是吧?
啥叫緩存?用內存當緩存。內存是無限的嗎,內存是很寶貴而且是有限的,磁盤是廉價而且是大量的。可能一台機器就幾十個 G 的內存,但是可以有幾個 T 的硬盤空間。redis 主要是基於內存來進行高性能、高並發的讀寫操作的。
那既然內存是有限的,比如 redis 就只能用 10G,你要是往里面寫了 20G 的數據,會咋辦?當然會干掉 10G 的數據,然后就保留 10G 的數據了。那干掉哪些數據?保留哪些數據?當然是干掉不常用的數據,保留常用的數據了。
數據明明過期了,怎么還占用着內存?
這是由 redis 的過期策略來決定。

二、分析

redis 過期策略
redis是單線程,收割時間也會占用線程處理時間,如果收割過於頻繁,會導致讀寫出現卡頓。

1、主庫過期策略

1.1、定時掃描
首先將每個設置了過期時間的key放到一個獨立的hash中,默認每秒定時遍歷這個hash而不是整個空間:
並不會遍歷所有的key,采用一種簡單的貪心策略
1.1.1、從過期key字典中,隨機找20個key。
1.1.2、刪除20gekey中過期的key
1.1.3、如果2中過期的key超過1/4,則重復第一步
1.1.4、每次處理的時間都不會25ms
如果有大量的key在同一時間段內過期,就會造成數據庫的集中訪問,就是緩存雪崩!
1.2、惰性策略
客戶端訪問的時候,會對這個key的過期時間進行檢查,如果過期了就立即刪除。惰性策略是對定時策略的補充,因為定時策略不會刪除所有過期的key

2、從庫過期策略

redis不會掃描從庫,刪除主庫數據的時候,在aof文件里生成一條del指令,在主從同步的時候,從庫會執行這條指令,刪除過期key。
所以集群分布式鎖算法的漏洞就是這樣產生的。

三、常見的幾種緩存失效策略 

FIFO ,first in first out ,最先進入緩存的數據在緩存空間不夠情況下(超出最大元素限制時)會被首先清理出去
LFU , Less Frequently Used ,一直以來最少被使用的元素會被被清理掉。這就要求緩存的元素有一個hit 屬性,在緩存空間不夠得情況下,hit 值最小的將會被清出緩存。
LRU ,Least Recently Used ,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。
 

四、緩存更新策略

更新緩存的設計模式有四種:Cache aside, Read through, Write through, Write behind caching

1、Cache aside

讀取:
失效:應用程序先從cache取數據,沒有得到,則從數據庫中取數據,成功后,放到緩存中。
命中:應用程序從cache中取數據,取到后返回。
更新:先把數據存到數據庫中,成功后,再讓緩存失效。
 
Read/Write Through Pattern
我們可以看到,在上面的Cache Aside套路中,我們的應用代碼需要維護兩個數據存儲,一個是緩存(Cache),一個是數據庫(Repository)。所以,應用程序比較啰嗦。而Read/Write Through套路是把更新數據庫(Repository)的操作由緩存自己代理了,所以,對於應用層來說,就簡單很多了。可以理解為,應用認為后端就是一個單一的存儲,而存儲自己維護自己的Cache。

 2、Read Through

Read Through 套路就是在查詢操作中更新緩存,也就是說,當緩存失效的時候(過期或LRU換出),Cache Aside是由調用方負責把數據加載入緩存,而Read Through則用緩存服務自己來加載,從而對應用方是透明的。

 3、Write Through

Write Through 套路和Read Through相仿,不過是在更新數據時發生。當有數據更新的時候,如果沒有命中緩存,直接更新數據庫,然后返回。如果命中了緩存,則更新緩存,然后再由Cache自己更新數據庫(這是一個同步操作)

 4、Write Behind Caching Pattern

Write Back套路,一句說就是,在更新數據的時候,只更新緩存,不更新數據庫,而我們的緩存會異步地批量更新數據庫。這個設計的好處就是讓數據的I/O操作飛快無比(因為直接操作內存嘛 ),因為異步,write backg還可以合並對同一個數據的多次操作,所以性能的提高是相當可觀的。
但是,其帶來的問題是,數據不是強一致性的。
redis 過期策略是:定期刪除+惰性刪除。
所謂定期刪除,指的是 redis 默認是每隔 100ms 就隨機抽取一些設置了過期時間的 key,檢查其是否過期,如果過期就刪除。
假設 redis 里放了 10w 個 key,都設置了過期時間,你每隔幾百毫秒,就檢查 10w 個 key,那 redis 基本上就死了,cpu 負載會很高的,消耗在你的檢查過期 key 上了。注意,這里可不是每隔 100ms 就遍歷所有的設置過期時間的 key,那樣就是一場性能上的災難。實際上 redis 是每隔 100ms 隨機抽取一些 key 來檢查和刪除的。
但是問題是,定期刪除可能會導致很多過期 key 到了時間並沒有被刪除掉,那咋整呢?所以就是惰性刪除了。這就是說,在你獲取某個 key 的時候,redis 會檢查一下 ,這個 key 如果設置了過期時間那么是否過期了?如果過期了此時就會刪除,不會給你返回任何東西。
獲取 key 的時候,如果此時 key 已經過期,就刪除,不會返回任何東西。
但是實際上這還是有問題的,如果定期刪除漏掉了很多過期 key,然后你也沒及時去查,也就沒走惰性刪除,此時會怎么樣?如果大量過期 key 堆積在內存里,導致 redis 內存塊耗盡了,咋整?
答案是:走內存淘汰機制。
內存淘汰機制
redis 內存淘汰機制有以下幾個:
  • noeviction: 不刪除策略, 達到最大內存限制時, 如果需要更多內存, 直接返回錯誤信息。 大多數寫命令都會導致占用更多的內存(有極少數會例外, 如 DEL )。
  • allkeys-lru:所有key通用; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
  • allkeys-random: 所有key通用; 隨機刪除一部分 key。
  • volatile-lru:只限於設置了 expire 的部分; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
  • volatile-random:只限於設置了 expire 的部分; 隨機刪除一部分 key。
  • volatile-ttl:只限於設置了 expire 的部分; 優先刪除剩余時間(time to live,TTL) 短的key。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM