Redis 面試常見問題———快取雪崩、快取擊穿以及快取穿透

小碼code發表於2021-10-18

在開發中會面臨快取異常可能會出現三個問題,分別是快取雪崩、快取擊穿和快取穿透。這三個問題會導致大量請求從快取轉移到資料庫,如果請求的併發量很大的話,就會導致資料庫崩潰。所以在面試官也會經常問這些問題。

快取雪崩

快取雪崩是指大量的請求無法在快取中處理,從而將請求轉移到資料庫中,導致資料壓力倍增。一個Redis例項可以支援萬級別的併發請求,而單個資料庫只能支援千級別的併發請求。兩者處理請求併發能力相差十倍,資料庫會由於壓力過大而導致雪崩。
這裡雪崩一般是由兩個原因組成,很多文章只寫快取同時過期的情況。
快取雪崩

原因一:快取中大量的資料同時過期

一般設定快取資料會設定快取時間,在某一時刻,大量的快取同時過期,此時如果有請求訪問這些資料的話,快取不存在,會將請求轉移到資料庫,如果這些的請求量比較大的,導致資料庫的壓力增大,嚴重會導致資料庫崩潰。

針對大量快取同時失效帶來的雪崩,有兩種解決方案。

方案一: 過期時間設定隨機值

應該避免給資料設定相同的過期時間,在設定過期時間時,增加一點隨機值。

setRedis(key, value, time + Math.random() * 10000);

方案二: 服務降級

服務降低,比如使用hystrix,是指發生雪崩時,針對不同的資料採取不同的處理方式。

  • 請求資料是非核心資料(比如商品屬性),暫時停止從快取查詢資料,直接返回預定資訊、空值或者錯誤值。
  • 請求資料是核心資料(比如商品庫存、價格),仍然查詢快取,如果快取缺失,繼續在資料庫讀取。
    服務降級

原因二: redis 服務掛了

redis服務發生當機,無法處理請求,這就會導致全部轉移到資料庫去,發生雪崩。

方案一: 新增服務熔斷

服務熔斷,就是發生雪崩時,暫停對快取的訪問。等redis服務恢復正常後,再允許訪問快取。
對redis所在的伺服器進行指標監控,比如QPS、CPU使用率、記憶體使用率等,如果發現redis服務當機,而資料庫請求壓力倍增,此時可以啟動熔斷機制,暫停對快取和資料庫的訪問,比如使用Hystrix服務熔斷。
但是暫停對快取系統的訪問,但是對整個業務系統影響很大,導致很多資料不能檢視。為了減少這種影響還有另一個方案:請求限流。

方案二: 請求限流

請求限流,就是限制前端請求每秒請求量,使得資料庫能承受前端全部請求。
比如前端允許每秒訪問1000次,其中900請求快取,100次才請求資料庫。
一旦發生雪崩,資料庫每秒請求激增到1000次,此時啟動請求限流,在前端入口只允許每秒請求100次,過多的請求直接拒絕。
請求限流
無論使用服務熔斷或請求限流都是是發生雪崩後處理,這裡還有事先預防的方案。
通過主從節點的方式構建redis叢集,如果redis主節點當機,從節點可以切換成主節點。

快取擊穿

快取擊穿是指,針對某個訪問快取非常頻繁,無法在快取中處理,訪問該資料的請求一下子都請求資料庫,導致資料庫壓力倍增。
快取擊穿

方案一:不設定過期時間

對於訪問特別頻繁的熱點資料,就不設定過期時間

方案二:使用互斥鎖

如果快取失效,只有拿到鎖才能訪問資料庫,降低資料庫併發訪問。

快取雪崩和快取擊穿的差別在於雪崩是大量的快取,擊穿是單一的快取。

快取穿透

快取穿透是指訪問的資料既不在redis快取中,也不在資料庫中,因為資料庫也不存在資料,也無法將資料庫資料寫入快取中,每次請求都要請求快取和伺服器。不過這樣也導致系統效能下降。
快取穿透會發生如下兩種情況:

  • 誤操作,刪除了快取和資料的資料。
  • 惡意攻擊: 專門訪問資料庫中不存在的資料。

快取穿透

方案一: 快取空值或預設值

發生快取穿透,在redis中快取一個空值或者實現預選設定好的值(比如0),後續請求查詢直接在redis中讀取空值或者預設值。

方案二: 使用布隆過濾器

  • 布隆過濾器由一個初值都為0的bit陣列和N個雜湊函式組成,可以用來快速判斷某個資料是否存在。當資料寫入資料庫時,布隆過濾器會通過三個操作完成標記:
    • 使用N個hash函式,分別計算這個資料的hash值,得到N個hash值。
    • 把這N個hash值對bit陣列的長度取模,得到每個hash值在陣列中對位置。
    • 把對應位置設定為1。如果資料不存在,那麼就沒用使用布隆過濾器標記過資料,那麼,bit陣列對應的bit位為零。只要bit陣列有一個不為1,就表明布隆過濾器就沒標記過該資料。
    • 把資料寫入資料庫時,使用布隆過濾器做標記
    • 當快取消失後,在去資料庫查詢之前,通過查詢布隆過濾器判斷資料是否存在,如果不存在,就不查詢資料庫。
      還有在請求入庫新增檢測,把惡意請求(引數不合理、引數非法、引數不存在或者id小於0)直接過濾掉。

電商系統舉例

  • 快取雪崩
    * 電商首頁資料(電腦端、手機端),比如分類。設定這一類的快取需要給過期時間新增隨機數
  • 快取擊穿
    • 電商首頁的猜你喜歡商品,不設定超時時間,或者設定互斥鎖
  • 快取穿透
    • 電商商品詳情中請求不存在的id,首先要設定入口驗證,然後使用布隆過濾器,不存在直接返回

總結

快取雪崩和快取擊穿主要是資料不在快取上,而快取穿透是資料既不在快取上,也不在資料上。

  • 快取雪崩
    • 給過期時間加上小的隨機數
    • 服務降級
    • 服務熔斷
    • 請求限流
    • redis 設定主從叢集
  • 快取擊穿
    • 不設定過期時間
  • 快取穿透
    • 入口進行合法性驗證
    • 使用空值或者預設值
    • 使用布隆過濾器快速判斷

預防方案

使用服務降低、請求熔斷、請求限制會影響使用者使用體驗,最好使用預防方案。

  • 針對快取雪崩,合理設定資料過期時間,以及搭建redis主從叢集。
  • 針對快取擊穿,不要設定過期時間。
  • 針對快取穿透,在請求入口做規範校驗,以及使用布隆過濾器判斷資料是否存在。

參考

相關文章