Redis緩存溢出處理

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

懂的越多,不會的也就越多,知識之路是不斷進取的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章