Redis緩存雪崩、緩存穿透、熱點Key解決方案和分析


緩存穿透

緩存系統,按照KEY去查詢VALUE,KEY對應的VALUE一定不存在的時候並對KEY並發請求量很大的時候,就會對后端造成很大的壓力。

(查詢一個必然不存在的數據。比如文章表,查詢一個不存在的id,每次都會訪問DB,如果有人惡意破壞,很可能直接對DB造成影響。)

由於緩存不命中,每次都要查詢持久層。從而失去緩存的意義。

 

解決方法:

1、緩存層緩存空值。 
–緩存太多空值,占用更多空間。(優化:給個空值過期時間) 
–存儲層更新代碼了,緩存層還是空值。(優化:后台設置時主動刪除空值,並緩存把值進去)

2、將數據庫中所有的查詢條件,放到布隆過濾器中。當一個查詢請求來臨的時候,先經過布隆過濾器進行檢查,如果請求存在這個條件中,那么繼續執行,如果不在,直接丟棄。

 

備注:

    比如數據庫中有10000個條件,那么布隆過濾器的容量size設置的要稍微比10000大一些,比如12000.

    對於誤判率的設置,根據實際項目,以及硬件設施來具體決定。但是一定不能設置為0,並且誤判率設置的越小,哈希函數跟數組長度都會更多跟更長,那么對硬件,內存中間的要求就會相應的高。

  private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size, 0.0001); 

    有了size跟誤判率,那么布隆過濾器就會產生相應的哈希函數跟數組。

    綜上:我們可以利用布隆過濾器,將redis緩存擊穿控制在一個可容忍的范圍內。

 


緩存雪崩(緩存失效)

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

        緩存層宕掉后,流量會像奔逃的野牛一樣,打向后端存儲

    解決方法:

  1. 在緩存失效后,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。
  2. 可以通過緩存reload機制,預先去更新緩存,再即將發生大並發訪問前手動觸發加載緩存
  3. 不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻
  4. 做二級緩存,或者雙緩存策略。A1為原始緩存,A2為拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置為短期,A2設置為長期。

 

 

熱點key

      (1) 這個key是一個熱點key(例如一個重要的新聞,一個熱門的八卦新聞等等),所以這種key訪問量可能非常大。

      (2) 緩存的構建是需要一定時間的。(可能是一個復雜計算,例如復雜的sql、多次IO、多個依賴(各種接口)等等)

       於是就會出現一個致命問題:在緩存失效的瞬間,有大量線程來構建緩存(見下圖),造成后端負載加大,甚至可能會讓系統崩潰 。

    解決方法:

1. 使用互斥鎖(mutex key):這種解決方案思路比較簡單,就是只讓一個線程構建緩存,其他線程等待構建緩存的線程執行完,重新從緩存獲取數據就可以了

2. "提前"使用互斥鎖(mutex key):在value內部設置1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已經過期時候,馬上延長timeout1並重新設置到cache。然后再從數據庫加載數據並設置到cache中。

3. "永遠不過期":

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

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

    (2) 從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value里,如果發現要過期了,通過一個后台的異步線程進行緩存的構建,也就是“邏輯”過期

4. 資源保護:可以做資源的隔離保護主線程池,如果把這個應用到緩存的構建也未嘗不可。

四種方案對比:

      作為一個並發量較大的互聯網應用,我們的目標有3個:

      1. 加快用戶訪問速度,提高用戶體驗。

      2. 降低后端負載,保證系統平穩。

      3. 保證數據“盡可能”及時更新(要不要完全一致,取決於業務,而不是技術。)

      所以第二節中提到的四種方法,可以做如下比較,還是那就話:沒有最好,只有最合適。 

解決方案 優點 缺點
簡單分布式鎖(Tim yang)

 1. 思路簡單

2. 保證一致性

1. 代碼復雜度增大

2. 存在死鎖的風險

3. 存在線程池阻塞的風險

加另外一個過期時間(Tim yang)  1. 保證一致性 同上 
不過期(本文)

1. 異步構建緩存,不會阻塞線程池

1. 不保證一致性。

2. 代碼復雜度增大(每個value都要維護一個timekey)。

3. 占用一定的內存空間(每個value都要維護一個timekey)。

資源隔離組件hystrix(本文)

1. hystrix技術成熟,有效保證后端。

2. hystrix監控強大。

 

 

1. 部分訪問存在降級策略。 


總結

 

   1.  熱點key + 過期時間 + 復雜的構建緩存過程 => mutex key問題

   2. 構建緩存一個線程做就可以了。

   3. 四種解決方案:沒有最佳只有最合適。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM