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

緩存穿透

緩存系統,按照KEY去查詢VALUE,當KEY對應的VALUE必定不存在的時候並對KEY併發請求量很大的時候,就會對後端形成很大的壓力。redis

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

因爲緩存不命中,每次都要查詢持久層。從而失去緩存的意義。數據庫

 

解決方法:後端

一、緩存層緩存空值。 
–緩存太多空值,佔用更多空間。(優化:給個空值過時時間) 
–存儲層更新代碼了,緩存層仍是空值。(優化:後臺設置時主動刪除空值,並緩存把值進去)數組

二、將數據庫中全部的查詢條件,放到布隆過濾器中。當一個查詢請求來臨的時候,先通過布隆過濾器進行檢查,若是請求存在這個條件中,那麼繼續執行,若是不在,直接丟棄。緩存

 

備註:併發

    好比數據庫中有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. 四種解決方案:沒有最佳只有最合適。

相關文章
相關標籤/搜索