Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级
一、緩存雪崩:
1、什么是緩存雪崩:
如果緩在某一個時刻出現(xiàn)大規(guī)模的key失效,那么就會導(dǎo)致大量的請求打在了數(shù)據(jù)庫上面,導(dǎo)致數(shù)據(jù)庫壓力巨大,如果在高并發(fā)的情況下,可能瞬間就會導(dǎo)致數(shù)據(jù)庫宕機。這時候如果運維馬上又重啟數(shù)據(jù)庫,馬上又會有新的流量把數(shù)據(jù)庫打死。這就是緩存雪崩。
2、問題分析:
造成緩存雪崩的關(guān)鍵在于同一時間的大規(guī)模的key失效,為什么會出現(xiàn)這個問題,主要有兩種可能:第一種是Redis宕機,第二種可能就是采用了相同的過期時間。搞清楚原因之后,那么有什么解決方案呢?
3、解決方案:
(1)事前:
① 均勻過期:設(shè)置不同的過期時間,讓緩存失效的時間盡量均勻,避免相同的過期時間導(dǎo)致緩存雪崩,造成大量數(shù)據(jù)庫的訪問。
② 分級緩存:第一級緩存失效的基礎(chǔ)上,訪問二級緩存,每一級緩存的失效時間都不同。
③ 熱點數(shù)據(jù)緩存永遠不過期。
永不過期實際包含兩層意思:
-
物理不過期,針對熱點key不設(shè)置過期時間
-
邏輯過期,把過期時間存在key對應(yīng)的value里,如果發(fā)現(xiàn)要過期了,通過一個后臺的異步線程進行緩存的構(gòu)建
④ 保證Redis緩存的高可用,防止Redis宕機導(dǎo)致緩存雪崩的問題。可以使用 主從+ 哨兵,Redis集群來避免 Redis 全盤崩潰的情況。
(2)事中:
① 互斥鎖:在緩存失效后,通過互斥鎖或者隊列來控制讀數(shù)據(jù)寫緩存的線程數(shù)量,比如某個key只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。這種方式會阻塞其他的線程,此時系統(tǒng)的吞吐量會下降
② 使用熔斷機制,限流降級。當(dāng)流量達到一定的閾值,直接返回“系統(tǒng)擁擠”之類的提示,防止過多的請求打在數(shù)據(jù)庫上將數(shù)據(jù)庫擊垮,至少能保證一部分用戶是可以正常使用,其他用戶多刷新幾次也能得到結(jié)果。
(3)事后:
① 開啟Redis持久化機制,盡快恢復(fù)緩存數(shù)據(jù),一旦重啟,就能從磁盤上自動加載數(shù)據(jù)恢復(fù)內(nèi)存中的數(shù)據(jù)。
?
二、緩存擊穿:
1、什么是緩存擊穿:
緩存擊穿跟緩存雪崩有點類似,緩存雪崩是大規(guī)模的key失效,而緩存擊穿是某個熱點的key失效,大并發(fā)集中對其進行請求,就會造成大量請求讀緩存沒讀到數(shù)據(jù),從而導(dǎo)致高并發(fā)訪問數(shù)據(jù)庫,引起數(shù)據(jù)庫壓力劇增。這種現(xiàn)象就叫做緩存擊穿。
2、問題分析:
關(guān)鍵在于某個熱點的key失效了,導(dǎo)致大并發(fā)集中打在數(shù)據(jù)庫上。所以要從兩個方面解決,第一是否可以考慮熱點key不設(shè)置過期時間,第二是否可以考慮降低打在數(shù)據(jù)庫上的請求數(shù)量。
3、解決方案:
(1)在緩存失效后,通過互斥鎖或者隊列來控制讀數(shù)據(jù)寫緩存的線程數(shù)量,比如某個key只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待。這種方式會阻塞其他的線程,此時系統(tǒng)的吞吐量會下降
(2)熱點數(shù)據(jù)緩存永遠不過期。
永不過期實際包含兩層意思:
-
物理不過期,針對熱點key不設(shè)置過期時間
-
邏輯過期,把過期時間存在key對應(yīng)的value里,如果發(fā)現(xiàn)要過期了,通過一個后臺的異步線程進行緩存的構(gòu)建
?
三、緩存穿透:
1、什么是緩存穿透:
緩存穿透是指用戶請求的數(shù)據(jù)在緩存中不存在即沒有命中,同時在數(shù)據(jù)庫中也不存在,導(dǎo)致用戶每次請求該數(shù)據(jù)都要去數(shù)據(jù)庫中查詢一遍。如果有惡意攻擊者不斷請求系統(tǒng)中不存在的數(shù)據(jù),會導(dǎo)致短時間大量請求落在數(shù)據(jù)庫上,造成數(shù)據(jù)庫壓力過大,甚至導(dǎo)致數(shù)據(jù)庫承受不住而宕機崩潰。
2、問題分析:
緩存穿透的關(guān)鍵在于在Redis中查不到key值,它和緩存擊穿的根本區(qū)別在于傳進來的key在Redis中是不存在的。假如有黑客傳進大量的不存在的key,那么大量的請求打在數(shù)據(jù)庫上是很致命的問題,所以在日常開發(fā)中要對參數(shù)做好校驗,一些非法的參數(shù),不可能存在的key就直接返回錯誤提示。
3、解決方法:
(1)將無效的key存放進Redis中:
當(dāng)出現(xiàn)Redis查不到數(shù)據(jù),數(shù)據(jù)庫也查不到數(shù)據(jù)的情況,我們就把這個key保存到Redis中,設(shè)置value="null",并設(shè)置其過期時間極短,后面再出現(xiàn)查詢這個key的請求的時候,直接返回null,就不需要再查詢數(shù)據(jù)庫了。但這種處理方式是有問題的,假如傳進來的這個不存在的Key值每次都是隨機的,那存進Redis也沒有意義。
(2)使用布隆過濾器:
如果布隆過濾器判定某個 key 不存在布隆過濾器中,那么就一定不存在,如果判定某個 key 存在,那么很大可能是存在(存在一定的誤判率)。于是我們可以在緩存之前再加一個布隆過濾器,將數(shù)據(jù)庫中的所有key都存儲在布隆過濾器中,在查詢Redis前先去布隆過濾器查詢 key 是否存在,如果不存在就直接返回,不讓其訪問數(shù)據(jù)庫,從而避免了對底層存儲系統(tǒng)的查詢壓力。
如何選擇:針對一些惡意攻擊,攻擊帶過來的大量key是隨機,那么我們采用第一種方案就會緩存大量不存在key的數(shù)據(jù)。那么這種方案就不合適了,我們可以先對使用布隆過濾器方案進行過濾掉這些key。所以,針對這種key異常多、請求重復(fù)率比較低的數(shù)據(jù),優(yōu)先使用第二種方案直接過濾掉。而對于空數(shù)據(jù)的key有限的,重復(fù)率比較高的,則可優(yōu)先采用第一種方式進行緩存。
?
四、緩存預(yù)熱:
1、什么是緩存預(yù)熱:
緩存預(yù)熱是指系統(tǒng)上線后,提前將相關(guān)的緩存數(shù)據(jù)加載到緩存系統(tǒng)。避免在用戶請求的時候,先查詢數(shù)據(jù)庫,然后再將數(shù)據(jù)緩存的問題,用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)。
如果不進行預(yù)熱,那么Redis初始狀態(tài)數(shù)據(jù)為空,系統(tǒng)上線初期,對于高并發(fā)的流量,都會訪問到數(shù)據(jù)庫中, 對數(shù)據(jù)庫造成流量的壓力。
2、緩存預(yù)熱解決方案:
(1)數(shù)據(jù)量不大的時候,工程啟動的時候進行加載緩存動作;
(2)數(shù)據(jù)量大的時候,設(shè)置一個定時任務(wù)腳本,進行緩存的刷新;
(3)數(shù)據(jù)量太大的時候,優(yōu)先保證熱點數(shù)據(jù)進行提前加載到緩存。
?
五、緩存降級:
緩存降級是指緩存失效或緩存服務(wù)器掛掉的情況下,不去訪問數(shù)據(jù)庫,直接返回默認數(shù)據(jù)或訪問服務(wù)的內(nèi)存數(shù)據(jù)。降級一般是有損的操作,所以盡量減少降級對于業(yè)務(wù)的影響程度。
在項目實戰(zhàn)中通常會將部分熱點數(shù)據(jù)緩存到服務(wù)的內(nèi)存中,這樣一旦緩存出現(xiàn)異常,可以直接使用服務(wù)的內(nèi)存數(shù)據(jù),從而避免數(shù)據(jù)庫遭受巨大壓力。
?
總結(jié)
以上是生活随笔為你收集整理的Redis的缓存雪崩、缓存击穿、缓存穿透与缓存预热、缓存降级的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring如何解决循环依赖问题
- 下一篇: 使用LinkedHashMap实现LRU