1. 程式人生 > >什麼是快取穿刺和快取雪崩?如何解決快取穿透,快取雪崩

什麼是快取穿刺和快取雪崩?如何解決快取穿透,快取雪崩

作為參考使用,以便查閱。

一.快取穿透:

     快取穿透是指查詢一個一定不存在的資料,由於快取是不命中時需要從資料庫查詢,查不到資料則不寫入快取,這將導致這個不存在的資料每次請求都要到資料庫去查詢,造成快取穿透。

     解決辦法:

     1.布隆過濾

  對所有可能查詢的引數以hash形式儲存,在控制層先進行校驗,不符合則丟棄。還有最常見的則是採用布隆過濾器,將所有可能存在的資料雜湊到一個足夠大的bitmap中,一個一定不存在的資料會被這個bitmap攔截掉,從而避免了對底層儲存系統的查詢壓力。

  補充:

      Bloom filter

  適用範圍:可以用來實現資料字典,進行資料的判重,或者集合求交集

  基本原理及要點:對於原理來說很簡單,位陣列+k個獨立hash函式。將hash函式對應的值的位陣列置1,查詢時如果發現所有hash函式對應位都是1說明存在,很明顯這個過程並不保證查詢的結果是100%正確的。同時也不支援刪除一個已經插入的關鍵字,因為該關鍵字對應的位會牽動到其他的關鍵字。所以一個簡單的改進就是counting Bloom filter,用一個counter陣列代替位陣列,就可以支援刪除了。新增時增加計數器,刪除時減少計數器。

     2. 快取空物件. 將 null 變成一個值.

  也可以採用一個更為簡單粗暴的方法,如果一個查詢返回的資料為空(不管是數 據不存在,還是系統故障),我們仍然把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。

 快取空物件會有兩個問題:

 第一,空值做了快取,意味著快取層中存了更多的鍵,需要更多的記憶體空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類資料設定一個較短的過期時間,讓其自動剔除。

 第二,快取層和儲存層的資料會有一段時間視窗的不一致,可能會對業務有一定影響。例如過期時間設定為 5分鐘,如果此時儲存層添加了這個資料,那此段時間就會出現快取層和儲存層資料的不一致,此時可以利用訊息系統或者其他方式清除掉快取層中的空物件。

二.快取雪崩

    如果快取集中在一段時間內失效,發生大量的快取穿透,所有的查詢都落在資料庫上,造成了快取雪崩。

    這個沒有完美解決辦法,但可以分析使用者行為,儘量讓失效時間點均勻分佈。大多數系統設計者考慮用加鎖或者佇列的方式保證快取的單執行緒(程序)寫,從而避免失效時大量的併發請求落到底層儲存系統上。

 解決方法

   1. 加鎖排隊. 限流-- 限流演算法. 1.計數 2.滑動視窗 3.  令牌桶Token Bucket 4.漏桶 leaky bucket [1]

 在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。

 業界比較常用的做法,是使用mutex。簡單地來說,就是在快取失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用快取工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作返回成功時,再進行load db的操作並回設快取;否則,就重試整個get快取的方法。

SETNX,是「SET if Not eXists」的縮寫,也就是隻有不存在的時候才設定,可以利用它來實現鎖的效果。

     2.資料預熱

  可以通過快取reload機制,預先去更新快取,再即將發生大併發訪問前手動觸發載入快取不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻

  3.做二級快取,或者雙快取策略。

     A1為原始快取,A2為拷貝快取,A1失效時,可以訪問A2,A1快取失效時間設定為短期,A2設定為長期。

   4.快取永遠不過期

 這裡的“永遠不過期”包含兩層意思:

    (1) 從快取上看,確實沒有設定過期時間,這就保證了,不會出現熱點key過期問題,也就是“物理”不過期。

     (2) 從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value裡,如果發現要過期了,通過一個後臺的非同步執行緒進行快取的構建,也就是“邏輯”過期.

 從實戰看,這種方法對於效能非常友好,唯一不足的就是構建快取時候,其餘執行緒(非構建快取的執行緒)可能訪問的是老資料,但是對於一般的網際網路功能來說這個還是可以忍受。