小白也能看懂的缓存雪崩、穿透、击穿
作為后端開發(fā),我想緩存是大家再熟悉不過的東西了。
我會介紹出現(xiàn)緩存雪崩、穿透和擊穿的業(yè)務(wù)背景、解決方案和對業(yè)務(wù)可靠性處理。事先說明,最佳解決方案一定需要結(jié)合實際業(yè)務(wù)調(diào)整,不同業(yè)務(wù)的處理不完全相同
其實我在網(wǎng)上也看過不少關(guān)于緩存雪崩、穿透、擊穿介紹,不知道是不是大家所做業(yè)務(wù)的不同,發(fā)現(xiàn)有不少小伙伴有以下疑問,比如:
加隨機時間過期后,如果訪問時間剛好就是加了隨機時間后的數(shù)據(jù),這樣豈不是白加了隨機時間?
熱點數(shù)據(jù)不過期,那豈不是有越來越多的臟數(shù)據(jù)?
就以上問題,我都會在文中一一解釋,以下說的緩存都指 Redis。
我爭取把這一高頻面試題講明白,如果大家看后能在這塊內(nèi)容和面試官面前談笑風生,那你就是最靚的仔。
下面,我就開始進入正題啦。
1. 緩存雪崩
即緩存同一時間大面積的失效,這個時候來了一大波請求,都懟到數(shù)據(jù)庫上,最后數(shù)據(jù)庫處理不過來崩了。
1.1 業(yè)務(wù)場景舉例
APP 首頁有大量熱點數(shù)據(jù),在某大型活動期間,針對不同時間段需要展示不同的首頁數(shù)據(jù)。
比如在 0 點時需要替換新的首頁數(shù)據(jù),此時舊首頁數(shù)據(jù)過期,新首頁數(shù)據(jù)剛開始加載。
而 0 點正在有個小活動開始,大批請求涌入。因為新數(shù)據(jù)剛開始加載,請求多數(shù)沒有命中緩存,請求到了數(shù)據(jù)庫,最后就把數(shù)據(jù)庫打掛了。
1.2 解決方案
再強調(diào)一下,所謂的解決方案是需要根據(jù)實際業(yè)務(wù)調(diào)整,不同業(yè)務(wù)的處理不完全相同
1.2.1 方法一
常見方式就是給過期時間加個隨機時間。
注意這個隨機時間不是幾秒哈,可以長達幾分鐘。因為如果數(shù)據(jù)量很大,按照上述例子,加上 Redis 是單線程處理數(shù)據(jù)的。那么幾秒的緩沖不一定能夠保證新數(shù)據(jù)都被加載完成。
所以過期時間寧愿設(shè)置長一點,也好過短一點。反正最后都是會過期掉,最終效果是一樣的。
而且過期時間范圍加大,key 會更加分散,這樣也是一定程度縮短 Redis 在過期 key 時候的阻塞時間。
而至于文章開頭說的:「如果訪問時間剛好就是加了隨機時間后的數(shù)據(jù),這樣豈不是白加了隨機時間」。
現(xiàn)在你結(jié)合上例活動的例子,它還會是一個問題嗎?結(jié)合業(yè)務(wù),一定要結(jié)合業(yè)務(wù)。
1.2.2 方法二
加互斥鎖,但這個方案會導(dǎo)致吞吐量明顯下降。所以還是要看實際業(yè)務(wù),像上述例子就不合適用
1.2.3 方法三
熱點數(shù)據(jù)不設(shè)置過期。不過期的話,正常業(yè)務(wù)請求自然就不會打到數(shù)據(jù)庫了。
那新的問題又來了,不過期有臟數(shù)據(jù),怎么辦?
很簡單,活動整體結(jié)束后再刪除嘛。
那像上述例子,可以怎么處理呢?
可以選擇方法一;或者提前把 0 點需要的新數(shù)據(jù)加載進 Redis,不必等到 0 點才去加載,這樣也是可以的
2. 緩存擊穿
緩存擊穿是指一個熱點 key 過期或被刪除后,導(dǎo)致線上原本能命中該熱點 key 的請求,瞬間大量地打到數(shù)據(jù)庫上,最終導(dǎo)致數(shù)據(jù)庫被擊垮。
有種千里之堤,潰于蟻穴的感覺。
2.1 業(yè)務(wù)場景舉例
出現(xiàn)情況一般是誤操作,比如設(shè)置錯了過期時間、誤刪除導(dǎo)致的。
誰還沒誤操作過呢,刪庫跑路了解一下。反正我誤刪過測試庫的數(shù)據(jù),幸好人沒事,狗頭保命。
2.2 解決方案
2.2.1?方法一
代碼問題,該 review 的 review。
熱點數(shù)據(jù)到底要不要過期,什么時候過期要明確
既然是熱點數(shù)據(jù),大概率是核心流程。那么該保證的核心功能還是需要保證的,減少犯錯機會。萬一出問題,那就是用戶的一波輸出了。
2.2.2 方法二
線上誤操作的事情,該加強權(quán)限管理的加強,特別是線上權(quán)限,一定需要審核,以防手抖。
3. 緩存穿透
緩存穿透是指:客戶端請求緩存和數(shù)據(jù)庫中不存在的數(shù)據(jù),導(dǎo)致所有的請求都打到數(shù)據(jù)庫上。如果請求很多,數(shù)據(jù)庫依舊會掛得明明白白。
3.1 業(yè)務(wù)場景舉例
數(shù)據(jù)庫主鍵 id 都是正數(shù),然后客戶端發(fā)起了?id = -1?的查詢
一個查詢接口,有一個狀態(tài)字段 status,其實 0 表示開始、1 表示結(jié)束。結(jié)果有請求一直發(fā) status=3 的請求過來
3.2 解決方案
3.2.1 方法一
做好參數(shù)校驗,對于不合理的參數(shù)要及時 return 結(jié)束。
這點非常重點,做任何業(yè)務(wù)都一樣,對于后端來說,要有互不信任原則。
簡單來說,就是不要信任來自前端、客戶端和上游服務(wù)的請求數(shù)據(jù),該做的校驗還是要做。
因為我們永遠都不知道用戶會寫什么奇奇怪怪的數(shù)據(jù);又或者即使你和對接的開發(fā)約定好了要怎么傳參數(shù),但你保不準他就沒遵守呢;退一步來說,萬一接口被破解呢。
你要保護好自己,不然到時出問題時,你和老大說,因為誰誰不遵守約定傳參導(dǎo)致,或者因為沒想到用戶會這么填,你看看你老大會這么說(狗頭.jpg)
3.2.2 方法二
對于查不到數(shù)據(jù)的 key,也將其短暫緩存起來。
比如 30s。這樣能避免大量相同請求瞬間打到數(shù)據(jù)庫上,減輕壓力。
但是后面肯定要去看為什么會有這樣的數(shù)據(jù),從根本上解決問題,該方法只是緩解問題而已。
如果發(fā)現(xiàn)就是某些 ip 在請求,并且這些數(shù)據(jù)非法,那可以在網(wǎng)關(guān)層限制這些 ip 訪問
3.2.3 方法三
提供一個能迅速判斷請求是否有效的攔截機制,比如布隆過濾器,Redis 本身就具有這個功能。
讓它維護所有合法的 key,如果請求參數(shù)不合法,則直接返回。否則就從緩存或數(shù)據(jù)庫中獲取。
關(guān)于布隆過濾器可以看我之前寫的文章:淺談布隆過濾器
4. 業(yè)務(wù)可靠性處理
如開頭所說,緩存指 Redis。
提高 Redis 可用性:Redis 要么用集群架構(gòu),要么用主從 + 哨兵。保證 Redis 的可用性。
沒有哨兵的主從不能自動故障轉(zhuǎn)移,所以只有主從,萬一高峰期或者在關(guān)鍵的活動時間節(jié)點掛了。
那么等出現(xiàn)線上告警、定位問題、溝通信息、等運維解決,一套操作下來,估計黃花菜都涼了。
減少對緩存的依賴
對于熱點數(shù)據(jù),是不是可以考慮加上本地緩存,比如:Guava、Ehcache,更簡單點,hashMap、List 什么也可以。
減少對 Redis 壓力的同時,還能提高性能,一舉兩得。
業(yè)務(wù)降級
從保護下游(接口或數(shù)據(jù)庫)的角度考慮,針對大流量場景是不是可以做下限流。這樣即使緩存崩了,也不至于把下游全部拖垮。
以及該降級的功能是不是可以降級,提前寫好降級開關(guān)和降級邏輯,關(guān)鍵時候全靠它穩(wěn)住。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的小白也能看懂的缓存雪崩、穿透、击穿的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 除阿里、网易和字节外,杭州居然还有这么多
- 下一篇: wpf esc key 检测不到_自己动