redis擊穿,穿透,雪崩以及解決方案

1 擊穿: 指的是單個key在緩存中查不到,去數據庫查詢,這樣如果數據量不大或者併發不大的話是沒有什麼問題的。

如果數據庫數據量大並且是高併發的情況下那麼就可能會造成數據庫壓力過大而崩潰

注意: 這裏指的是單個key發生高併發!!!

解決方案:

1) 通過synchronized+雙重檢查機制:某個key只讓一個線程查詢,阻塞其它線程

 在同步塊中,繼續判斷檢查,保證不存在,纔去查DB。(這個跟單例模式裏的double check是一個道理)

例如:

private static volaite Object lockHelp=new Object();

public String getValue(String key){

 String value=redis.get(key,String.class);

 if(value=="null"||value==null||StringUtils.isBlank(value){

     synchronized(lockHelp){

            value=redis.get(key,String.class);

             if(value=="null"||value==null||StringUtils.isBlank(value){

                 value=db.query(key);

                  redis.set(key,value,1000);

              }

        }

       }    

    return value;

}

缺點: 會阻塞其它線程

2) 設置value永不過期

   這種方式可以說是最可靠的,最安全的但是佔空間,內存消耗大,並且不能保持數據最新 這個需要根據具體的業務邏輯來做 

 個人覺得如果要保持數據最新不放這麼試試,僅供參考:

  起個定時任務或者利用TimerTask 做定時,每個一段時間多這些值進行數據庫查詢更新一次緩存,當然前提時不會給數據庫造成壓力過大(這個很重要)

3) 使用互斥鎖(mutex key)

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

SETNX,是「SET if Not eXists」的縮寫,也就是隻有不存在的時候才設置,可以利用它來實現鎖的效果。在redis2.6.1之前版本未實現setnx的過期時間,所以這裏給出兩種版本代碼參考:

public String get(key) {
String value = redis.get(key);
if (value == null) { //代表緩存值過期
//設置3min的超時,防止del操作失敗的時候,下次緩存過期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表設置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);

                 return value;
          } else {  //這個時候代表同時候的其他線程已經load db並回設到緩存了,這時候重試獲取緩存值即可
                  sleep(10);
                  get(key);  //重試
          }
      } else {
          return value;      
      }

}

缺點:

  1. 代碼複雜度增大

  2. 存在死鎖的風險

  3. 存在線程池阻塞的風險
    ps:這裏主要就是爲了解決,多機分佈式部署的情況下,視情況而定是不是需要加全局鎖

2 雪崩

雪崩指的是多個key查詢並且出現高併發,緩存中失效或者查不到,然後都去db查詢,從而導致db壓力突然飆升,從而崩潰。

出現原因: 1 key同時失效

             2 redis本身崩潰了

方案:

在緩存失效後,通過加鎖或者隊列來控制讀數據庫寫緩存的線程數量。比如對某個key只允許一個線程查詢數據和寫緩存,其他線程等待。(跟擊穿的第一個方案類似,但是這樣是避免不了其它key去查數據庫,只能減少查詢的次數)
可以通過緩存reload機制,預先去更新緩存,再即將發生大併發訪問前手動觸發加載緩存
不同的key,設置不同的過期時間,具體值可以根據業務決定,讓緩存失效的時間點儘量均勻
做二級緩存,或者雙緩存策略。A1爲原始緩存,A2爲拷貝緩存,A1失效時,可以訪問A2,A1緩存失效時間設置爲短期,A2設置爲長期。(這種方式複雜點)
3 擊透

一般是出現這種情況是因爲惡意頻繁查詢纔會對系統造成很大的問題: key緩存並且數據庫不存在,所以每次查詢都會查詢數據庫從而導致數據庫崩潰。

解決方案:

      1) 使用布隆過濾器: 熱點數據等場景(具體看使用場景)

布隆過濾器是什麼?

布隆過濾器可以理解爲一個不怎麼精確的 set 結構,當你使用它的 contains 方法判斷某個對象是否存在時,它可能會誤判。但是布隆過濾器也不是特別不精確,只要參數設置的合理,它的精確度可以控制的相對足夠精確,只會有小小的誤判概率。

當布隆過濾器說某個值存在時,這個值可能不存在;當它說不存在時,那就肯定不存在。打個比方,當它說不認識你時,肯定就不認識;當它說見過你時,可能根本就沒見過面,不過因爲你的臉跟它認識的人中某臉比較相似 (某些熟臉的係數組合),所以誤判以前見過你。

缺點: 1 會存在一定的誤判率

      2  對新增加的數據無法進行布隆過濾

      3 數據的key不會頻繁的更改

google 的 gauva 中有布隆過濾的實現

 BloomFilter的關鍵在於hash算法的設定和bit數組的大小確定,通過權衡得到一個錯誤概率可以接受的結果。

 我們設置的容錯率越小那麼過濾函數也就多,分配的空間也就越大(存放bits),那麼誤判率也就越小。

2 將擊透的key緩存起來,但是時間不能太長,下次進來是直接返回不存在,但是這種情況無法過濾掉動態的key,就是說每次請求進來都是不同額key,這樣還是會造成這個問題

覺得文章有幫助的話就讚賞下吧!

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