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

  • 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主從集群。
  • 針對快取擊穿,不要設置過期時間。
  • 針對快取穿透,在請求入口做規範校驗,以及使用布隆過濾器判斷數據是否存在。

參考