小白也能看懂的快取雪崩、穿透、擊穿

七淅在學Java發表於2022-03-27

大家好,我是七淅(xī)。

作為後端開發,我想快取是大家再熟悉不過的東西了。

本文會介紹出現快取雪崩、穿透和擊穿的業務背景、解決方案和對業務可靠性處理。事先說明,最佳解決方案一定需要結合實際業務調整,不同業務的處理不完全相同

其實我在網上也看過不少關於快取雪崩、穿透、擊穿介紹,不知道是不是大家所做業務的不同,發現有不少小夥伴有以下疑問,比如:

  • 加隨機時間過期後,如果訪問時間剛好就是加了隨機時間後的資料,這樣豈不是白加了隨機時間?
  • 熱點資料不過期,那豈不是有越來越多的髒資料?

就以上問題,我都會在文中一一解釋,以下說的快取都指 Redis。

我爭取把這一高頻面試題講明白,如果大家看後能在這塊內容和麵試官面前談笑風生,那你就是最靚的仔。

下面,我就開始進入正題啦。

1. 快取雪崩

即快取同一時間大面積的失效,這個時候來了一大波請求,都懟到資料庫上,最後資料庫處理不過來崩了。

1.1 業務場景舉例

APP 首頁有大量熱點資料,在某大型活動期間,針對不同時間段需要展示不同的首頁資料。

比如在 0 點時需要替換新的首頁資料,此時舊首頁資料過期,新首頁資料剛開始載入。

而 0 點正在有個小活動開始,大批請求湧入。因為新資料剛開始載入,請求多數沒有命中快取,請求到了資料庫,最後就把資料庫打掛了。

1.2 解決方案

再強調一下,所謂的解決方案是需要根據實際業務調整,不同業務的處理不完全相同

1.2.1 方法一

常見方式就是給過期時間加個隨機時間。

注意這個隨機時間不是幾秒哈,可以長達幾分鐘。因為如果資料量很大,按照上述例子,加上 Redis 是單執行緒處理資料的。那麼幾秒的緩衝不一定能夠保證新資料都被載入完成。

所以過期時間寧願設定長一點,也好過短一點。反正最後都是會過期掉,最終效果是一樣的。

而且過期時間範圍加大,key 會更加分散,這樣也是一定程度縮短 Redis 在過期 key 時候的阻塞時間。

而至於文章開頭說的:「如果訪問時間剛好就是加了隨機時間後的資料,這樣豈不是白加了隨機時間」。

現在你結合上例活動的例子,它還會是一個問題嗎?結合業務,一定要結合業務。

1.2.2 方法二

加互斥鎖,但這個方案會導致吞吐量明顯下降。所以還是要看實際業務,像上述例子就不合適用

1.2.3 方法三

熱點資料不設定過期。不過期的話,正常業務請求自然就不會打到資料庫了。

那新的問題又來了,不過期有髒資料,怎麼辦?

很簡單,活動整體結束後再刪除嘛。

那像上述例子,可以怎麼處理呢?—— 選擇方法一;或者提前把 0 點需要的新資料載入進 Redis,不必等到 0 點才去載入,這樣也是可以的

2. 快取擊穿

快取擊穿是指一個熱點 key 過期或被刪除後,導致線上原本能命中該熱點 key 的請求,瞬間大量地打到資料庫上,最終導致資料庫被擊垮。

有種千里之堤,潰於蟻穴的感覺。

2.1 業務場景舉例

出現情況一般是誤操作,比如設定錯了過期時間、誤刪除導致的。

誰還沒誤操作過呢,刪庫跑路瞭解一下。反正我誤刪過測試庫的資料,幸好人沒事,狗頭保命。

2.2 解決方案

方法一

程式碼問題,該 review 的 review。

熱點資料到底要不要過期,什麼時候過期要明確

既然是熱點資料,大概率是核心流程。那麼該保證的核心功能還是需要保證的,減少犯錯機會。萬一出問題,那就是使用者的一波輸出了。

方法二

線上誤操作的事情,該加強許可權管理的加強,特別是線上許可權,一定需要稽核,以防手抖。

PS:若有幫助希望大家可以點贊、在看、轉發隨便來一份鼓勵吧,這對我真得很重要,非常感謝~

3. 快取穿透

快取穿透是指:客戶端請求快取和資料庫中不存在的資料,導致所有的請求都打到資料庫上。如果請求很多,資料庫依舊會掛得明明白白。

3.1 業務場景舉例

  • 資料庫主鍵 id 都是正數,然後客戶端發起了 id = -1 的查詢
  • 一個查詢介面,有一個狀態欄位 status,其實 0 表示開始、1 表示結束。結果有請求一直髮 status=3 的請求過來

3.2 解決方案

3.2.1 方法一

做好引數校驗,對於不合理的引數要及時 return 結束

這點非常重點,做任何業務都一樣,對於後端來說,要有互不信任原則

簡單來說,就是不要信任來自前端、客戶端和上游服務的請求資料,該做的校驗還是要做。

因為我們永遠都不知道使用者會寫什麼奇奇怪怪的資料;又或者即使你和對接的開發約定好了要怎麼傳引數,但你保不準他就沒遵守呢;退一步來說,萬一介面被破解呢。

你要保護好自己,不然到時出問題時,你和老大說,因為誰誰不遵守約定傳參導致,或者因為沒想到使用者會這麼填,你看看你老大會這麼說(狗頭.jpg)

3.2.2 方法二

對於查不到資料的 key,也將其短暫快取起來。

比如 30s。這樣能避免大量相同請求瞬間打到資料庫上,減輕壓力。

但是後面肯定要去看為什麼會有這樣的資料,從根本上解決問題,該方法只是緩解問題而已。

如果發現就是某些 ip 在請求,並且這些資料非法,那可以在閘道器層限制這些 ip 訪問

3.2.3 方法三

提供一個能迅速判斷請求是否有效的攔截機制,比如布隆過濾器,Redis 本身就具有這個功能。

讓它維護所有合法的 key,如果請求引數不合法,則直接返回。否則就從快取或資料庫中獲取。

關於布隆過濾器可以看我之前寫的文章:布隆過濾器

4. 業務可靠性處理

如開頭所說,快取指 Redis。

  • 提高 Redis 可用性:Redis 要麼用叢集架構,要麼用主從 + 哨兵。保證 Redis 的可用性。

沒有哨兵的主從不能自動故障轉移,所以只有主從,萬一高峰期或者在關鍵的活動時間節點掛了。

那麼等出現線上告警、定位問題、溝通訊息、等運維解決,一套操作下來,估計黃花菜都涼了。

  • 減少對快取的依賴

對於熱點資料,是不是可以考慮加上本地快取,比如:Guava、Ehcache,更簡單點,hashMap、List 什麼也可以。

減少對 Redis 壓力的同時,還能提高效能,一舉兩得。

  • 業務降級

從保護下游(介面或資料庫)的角度考慮,針對大流量場景是不是可以做下限流。這樣即使快取崩了,也不至於把下游全部拖垮。

以及該降級的功能是不是可以降級,提前寫好降級開關和降級邏輯,關鍵時候全靠它穩住。

原創不易,如果覺得文章不錯,希望能關注下我的公號:七淅在學Java

相關文章