一、緩存穿透:(即:緩存無數據,數據庫也無數據)
如黑客惡意攻擊,使用緩存和數據庫均沒有的key進行不斷請求,導致數據庫壓力過大。
解決方法:
1、對用戶進行鑒權、對請求參數進行校驗,不合理直接過濾。
2、對查詢不到的數據也放到緩存,value為空,設置一定的過期時間。(不太常用,因為如果是隨機key就不起作用,且占緩存)
3、使用布隆過濾器,快速判斷key是否在數據庫中存在,不存在直接返回。(最有效)
第1種是最常用的策略,第2種不太常用,因為如果是隨機key就不起作用,且占緩存,第三種最簡單有效。實際使用中,可以1、3相結合。
二、緩存擊穿:(即:緩存無數據,數據庫有數據,key比較集中)
如高並發的情況下,熱點數據緩存過期,這時候會導致大量請求讀不到緩存同時讀數據庫,導致數據庫負載過大。
解決方法:
1、設置熱點數據永遠不過期。
2、熱點數據快過期時,通過另一個異步線程重新設置key。
3、當從緩存數據過期,重新從數據庫加載數據到緩存的過程上互斥鎖。
第1種的話,數據量大時,緩存量會比較大,第2種,很好理解,但是需要另外的邏輯去維護,會增加系統的復雜度。第3種,是比較常用的方式。
加載緩存時上互斥鎖:
1 public String get(String key) throws Exception { 2 String value = redis.get(key); 3 // 緩存過期 4 if (value == null) { 5 // 設置有效期,防止del操作失敗時,緩存過期一直不能重新加載緩存 6 if (redis.setnx(key_mutex, 1, 60)) { 7 // 從數據庫加載緩存 8 value = database.get(key); 9 redis.set(key, value, expire_time); 10 redis.del(key_mutex); 11 } else { 12 // 其他線程已經在加載緩存,等待並重新獲取即可 13 sleep(50); 14 get(key); 15 } 16 } 17 return value; 18 }
三、緩存雪崩:(即:緩存無數據,數據庫有數據,key比較分散)
如在高並發的情況下,緩存同一時刻失效(如緩存掛了,或者設置了相同過期時間),所有請求會讀數據庫,容易導致數據庫負載瞬間上升,乃至崩掉。如果重啟數據庫,立馬又會被新的請求壓崩。
解決方法:
1、緩存的失效時間設置為隨機值,避免同時失效。
2、redis搭建高可用,主從+哨兵,redis cluster。
3、服務限流、降級,避免數據庫被瞬間壓崩。
第1種只能防止因緩存同時過期導致的緩存失效,第2種可以有效避免單台緩存掛掉的情況。第3種是通過提高服務的高可用,來避免緩存失效帶來的影響,是輔助措施。
緩存擊穿和緩存雪崩區別:
兩者有點像,主要區別在於緩存擊穿是查詢同一條數據或熱點數據查詢不到,穿過了緩存。緩存雪崩是指大量數據查詢不到,穿過了緩存。