Redis快取穿透,擊穿和雪崩

worshipone發表於2024-04-25

快取穿透

快取穿透是指查詢一個一定不存在的資料,如果從儲存層查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到 DB 去查詢,可能導致 DB 掛掉。這種情況大機率是遭到了攻擊。
解決方案的話,我們通常都會用布隆過濾器來解決它。
布隆過濾器主要是用於檢索一個元素是否在一個集合中。我們當時使用的是redisson實現的布隆過濾。
它的底層主要是先去初始化一個比較大陣列,裡面存放的二進位制0或1。在一開始都是0,當一個key來了之後經過3次hash計算,模於陣列長度找到資料的下標然後把陣列中原來的0改為1,這樣的話,三個陣列的位置就能標明一個key的存在。查詢的過程也是一樣的。
當然是有缺點的,布隆過濾器有可能會產生一定的誤判,我們一般可以設定這個誤判率,大概不會超過5%,其實這個誤判是必然存在的,要不就得增加陣列的長度,其實已經算是很劃分了,5%以內的誤判率一般的專案也能接受,不至於高併發下壓倒資料庫。

快取擊穿

快取擊穿的意思是對於設定了過期時間的key,快取在某個時間點過期的時候,恰好這時間點對這個Key有大量的併發請求過來,這些請求發現快取過期一般都會從後端 DB 載入資料並回設到快取,這個時候大併發的請求可能會瞬間把 DB 壓垮。
解決方案有兩種方式:
第一可以使用互斥鎖:當快取失效時,不立即去load db,先使用如 Redis 的 setnx 去設定一個互斥鎖,當操作成功返回時再進行 load db的操作並回設快取,否則重試get快取的方法
第二種方案可以設定當前key邏輯過期,大概是思路如下:

  1. 在設定key的時候,設定一個過期時間欄位一塊存入快取中,不給當前key設定過期時間
  2. 當查詢的時候,從redis取出資料後判斷時間是否過期
  3. 如果過期則開通另外一個執行緒進行資料同步,當前執行緒正常返回資料,這個資料不是最新
    當然兩種方案各有利弊:
    如果選擇資料的強一致性,建議使用分散式鎖的方案,效能上可能沒那麼高,鎖需要等,也有可能產生死鎖的問題
    如果選擇key的邏輯刪除,則優先考慮的高可用性,效能比較高,但是資料同步這塊做不到強一致。

快取雪崩

快取雪崩意思是設定快取時採用了相同的過期時間,導致快取在某一時刻同時失效,請求全部轉發到DB,DB 瞬時壓力過重雪崩。與快取擊穿的區別:雪崩是很多key,擊穿是某一個key快取。
解決方案主要是可以將快取失效時間分散開,比如可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個快取的過期時間的重複率就會降低,就很難引發集體失效的事件。

相關文章