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

1.緩存穿透

一個緩存系統,正常情況下我們去查詢的時候大部分key都是存在的。
如果去請求一個緩存系統中沒有的數據,此時系統如果沒有經過優化,會將請求繼續打到數據庫上,但其實數據庫上也沒有這條數據。上面這種情況就叫緩存穿透。
如果有人要對系統進行攻擊,拿大量不存在的key去發起請求,你的系統將會產生大量的請求直接訪問數據庫,這樣數據庫的壓力太大很有可能直接宕機,整個服務直接掛掉。

針對上面這種情況,一般有如下的解決辦法。
如果對應的key空數據量級不大,且重複訪問請求比較多,那麼我們可以將這麼key對應的value全部設爲null加入緩存中。這樣該key只有第一次請求時候訪問數據庫,後面再出現這個key的請求時候直接返回null,不會再去訪問數據庫。
如果異常的key很多,且沒有什麼重複請求,這個時候如果我們將這些key都緩存起來,將會浪費大量的內存。這個時候,可以在緩存之前先加一層BloomFilter,查詢的時候先去BloomFilter中判斷key是否存在。如果key不存在可以直接返回。如果key存在再去查緩存或者查後端數據庫。

2.緩存擊穿

對於高併發系統,如果有大量的請求同時去查詢一個key,剛好這個key失效,如果沒有優化大量的請求也會直接打到後端數據庫,有可能導致宕機,這個叫緩存擊穿。

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

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

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

3.緩存雪崩

當緩存系統某一時刻發生大規模的緩存失效的情況,比如你的緩存服務宕機了,會有大量的請求進來直接打到DB上面。結果就是DB hold不住,宕機了,這個就叫緩存雪崩。

針對這種情況,首先我們需要保證的是緩存服務的高可用性。比如現在redis一般是集羣模式,不會是點個服務器。另外,關鍵服務部分可能還會使用主從+哨兵備份集羣,避免redis服務全部掛掉。

使用 ehcache 本地緩存的目的也是考慮在 Redis Cluster 完全不可用的時候,ehcache 本地緩存還能夠支撐一陣。使用 Hystrix進行限流 & 降級 ,比如一秒來了5000個請求,我們可以設置假設只能有一秒 2000個請求能通過這個組件,那麼其他剩餘的 3000 請求就會走限流邏輯。然後去調用我們自己開發的降級組件(降級),比如設置的一些默認值呀之類的。以此來保護最後的 MySQL 不會被大量的請求給打死。

另外實際中還有一個簡單的方案就是使緩存失效的時間分開。比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。

4.RDB與AOF

RDB是redis database。其工作原理就是在指定時間間隔內將內存中的數據集快照寫入磁盤,相當於是全量備份,所以有點在於數據集小,數據備份,遷移都比較方便,而且恢復起來速度也快。但是缺點也顯而易見,如果快照的時間間隔比較大,那數據丟失也比較多。因爲是全量備份,當數據量較大的時候,耗時也會比較長。
AOF是append only file, 是把每一次數據變化都寫入到一個aof裏面。以日誌的形式記錄每個操作,將Redis執行過的所有指令全部記錄下來(讀操作不記錄),只許追加文件但不可以修改文件,Redis啓動時會讀取AOF配置文件重構數據。換句話說,就是Redis重啓就會根據日誌內容從頭到尾執行一次來完成數據的恢復工作。AOF的有點是數據丟失比較少而且文件可讀,缺點是文件增長比較快,因爲記錄的是redis的操作日誌,同時恢復起來速度也會比較慢。

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