Redis緩存穿透,雪崩,擊穿現象與解決辦法
一.緩存穿透
1.什么是緩存穿透
緩存穿透,是指查詢一個數據庫一定不存在的數據。
例如:對於系統A,假設一秒 5000 個請求,結果其中 4000 個請求是黑客發出的惡意攻擊。黑客發出的那 4000 個攻擊,緩存中查不到,每次你去數據庫里查,也查不到。這種惡意攻擊場景的緩存穿透就會直接把數據庫給打死
2.解決辦法
(1)采用布隆過濾器BloomFilter
將所有可能存在的數據哈 希到一個足夠大的 bitmap 中,一個一定不存在的數據會被這個 bitmap 攔截掉,從而避免了對底層存儲系統的查詢壓力
(2)緩存空值
如果一個查詢返回的數據為空(不管是數據不 存在,還是系統故障)我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鍾。 通過這個直接設置的默認值存放到緩存,這樣第二次到緩沖中獲取就有值了,而不會繼續訪問數據庫
(3)接口限流與熔斷、降級
重要的接口一定要做好限流策略,防止用戶惡意刷接口,同時要降級准備,當接口中的某些服務不可用時候,進行熔斷,失敗快速返回機制。
-------------------------------------------- 黃金切割線 --------------------------------------------
二.緩存雪崩
1.什么是緩存雪崩
緩存雪崩,是指在某一個時間段,緩存集中過期失效。
舉個栗子:如果首頁所有 Key 的失效時間都是 12 小時,中午 12 點刷新的,我零點有個大促活動大量用戶涌入,假設每秒 6000 個請求,本來緩存可以抗住每秒 5000 個請求,但是緩存中所有 Key 都失效了。此時 6000 個/秒的請求全部落在了數據庫上,數據庫必然扛不住,真實情況可能 DBA 都沒反應過來直接掛了。
2.解決辦法
(1)加鎖排隊
key: whiltList value:1000w個uid 指定setNx whiltList value nullValue mutex互斥鎖解決,Redis的SETNX去set一個mutex key, 當操作返回成功時,再進行load db的操作並回設緩存; 否則,就重試整個get緩存的方法
(2) 數據預熱
緩存預熱就是系統上線后,將相關的緩存數據直接加載到緩存系統。這樣就可以避免在用戶請求的時候,先查詢數據庫,然后再將數據緩存的問題!用戶直接查詢事先被預熱的緩存數據!可以通過緩存reload機制,預先去更新緩存,再即將發生大並發訪問前手動觸發加載緩存不同的key
(3) 雙層緩存策略
C1為原始緩存,C2為拷貝緩存,C1失效時,可以訪問C2,C1緩存失效時間設置為短期,C2設置為長期。
(4) 定時更新緩存策略
失效性要求不高的緩存,容器啟動初始化加載,采用定時任務更新或移除緩存,
(5)不同過期時間
設置不同的過期時間,讓緩存失效的時間點盡量均勻
(6)熱點數據永不過期
部分用戶訪問特別頻繁的熱點數據,設置永不過期
-------------------------------------------- 黃金切割線 --------------------------------------------
三.緩存擊穿
1.什么是緩存擊穿
緩存擊穿是指一個 Key 非常熱點,在不停地扛着大量的請求,大並發集中對這一個點進行訪問,當這個 Key 在失效的瞬間,持續的大並發直接落到了數據庫上,就在這個 Key 的點上擊穿了緩存。
2.解決辦法
(1)互斥鎖
public static String getData(String key) throws InterruptedException {
//從Redis查詢數據
String result = getDataByKV(key);
//參數校驗
if (StringUtils.isBlank(result)) {
try {
//獲得鎖
if (reenLock.tryLock()) {
//去數據庫查詢
result = getDataByDB(key);
//校驗
if (StringUtils.isNotBlank(result)) {
//插進緩存
setDataToKV(key, result);
}
} else {
//睡一會再拿
Thread.sleep(100L);
result = getData(key);
}
} finally {
//釋放鎖
reenLock.unlock();
}
}
return result;
}
(2)熱點數據永不過期
不過期將不存在該問題