Redis緩存雪崩、穿透、擊穿三步曲
本文收集了一些 Redis 使用中經常遇到的一些問題,和與之相對應的解決方案,這些內容不但會出現在實際工作中,也是面試的高頻問題,接下來一起來看。

一、緩存雪崩
緩存雪崩是指在短時間內,有大量緩存同時過期,導致大量的請求直接查詢數據庫,從而對數據庫造成了巨大的壓力,嚴重情況下可能會導致數據庫宕機的情況叫做緩存雪崩。
我們先來看下正常情況下和緩存雪崩時程序的執行流程圖,正常情況下系統的執行流程如下圖所示:

緩存雪崩的執行流程,如下圖所示:

以上對比圖可以看出緩存雪崩對系統造成的影響,那如何解決緩存雪崩的問題?
緩存雪崩的常用解決方案有以下幾個。
1.加鎖排隊
加鎖排隊可以起到緩沖的作用,防止大量的請求同時操作數據庫,但它的缺點是增加了系統的響應時間,降低了系統的吞吐量,犧牲了一部分用戶體驗。
加鎖排隊的代碼實現,如下所示:
// 緩存 key
String cacheKey = "userlist";
// 查詢緩存
String data = jedis.get(cacheKey);
if (StringUtils.isNotBlank(data)) {
// 查詢到數據,直接返回結果
return data;
} else {
// 先排隊查詢數據庫,在放入緩存
synchronized (cacheKey) {
data = jedis.get(cacheKey);
if (!StringUtils.isNotBlank(data)) { // 雙重判斷
// 查詢數據庫
data = findUserInfo();
// 放入緩存
jedis.set(cacheKey, data);
}
return data;
}
}以上為加鎖排隊的實現示例,讀者可根據自己的實際項目情況做相應的修改。
2.隨機化過期時間
為了避免緩存同時過期,可在設置緩存時添加隨機時間,這樣就可以極大的避免大量的緩存同時失效。
示例代碼如下:
// 緩存原本的失效時間
int exTime = 10 * 60;
// 隨機數生成類
Random random = new Random();
// 緩存設置
jedis.setex(cacheKey, exTime+random.nextInt(1000) , value);3.設置二級緩存
二級緩存指的是除了 Redis 本身的緩存,再設置一層緩存,當 Redis 失效之后,先去查詢二級緩存。
例如可以設置一個本地緩存,在 Redis 緩存失效的時候先去查詢本地緩存而非查詢數據庫。
加入二級緩存之后程序執行流程,如下圖所示:

二、緩存穿透
緩存穿透是指查詢數據庫和緩存都無數據,因為數據庫查詢無數據,出于容錯考慮,不會將結果保存到緩存中,因此每次請求都會去查詢數據庫,這種情況就叫做緩存穿透。
緩存穿透執行流程如下圖所示:

其中紅色路徑表示緩存穿透的執行路徑,可以看出緩存穿透會給數據庫造成很大的壓力。
緩存穿透的解決方案有以下幾個。
1.使用過濾器
我們可以使用過濾器來減少對數據庫的請求,例如使用我們前面章節所學的布隆過濾器,我們這里簡單復習一下布隆過濾器,它的原理是將數據庫的數據哈希到 bitmap 中,每次查詢之前,先使用布隆過濾器過濾掉一定不存在的無效請求,從而避免了無效請求給數據庫帶來的查詢壓力。
2.緩存空結果
另一種方式是我們可以把每次從數據庫查詢的數據都保存到緩存中,為了提高前臺用戶的使用體驗 (解決長時間內查詢不到任何信息的情況),我們可以將空結果的緩存時間設置得短一些,例如 3~5 分鐘。
三、緩存擊穿
緩存擊穿指的是某個熱點緩存,在某一時刻恰好失效了,然后此時剛好有大量的并發請求,此時這些請求將會給數據庫造成巨大的壓力,這種情況就叫做緩存擊穿。
緩存擊穿的執行流程如下圖所示:

它的解決方案有以下幾個。
1.加鎖排隊
此處理方式和緩存雪崩加鎖排隊的方法類似,都是在查詢數據庫時加鎖排隊,緩沖操作請求以此來減少服務器的運行壓力。
2.設置永不過期
對于某些熱點緩存,我們可以設置永不過期,這樣就能保證緩存的穩定性,但需要注意在數據更改之后,要及時更新此熱點緩存,不然就會造成查詢結果的誤差。
3.緩存預熱
首先來說,緩存預熱并不是一個問題,而是使用緩存時的一個優化方案,它可以提高前臺用戶的使用體驗。
緩存預熱指的是在系統啟動的時候,先把查詢結果預存到緩存中,以便用戶后面查詢時可以直接從緩存中讀取,以節約用戶的等待時間。
緩存預熱的執行流程,如下圖所示:

緩存預熱的實現思路有以下三種:
- 把需要緩存的方法寫在系統初始化的方法中,這樣系統在啟動的時候就會自動的加載數據并緩存數據;
- 把需要緩存的方法掛載到某個頁面或后端接口上,手動觸發緩存預熱;
- 設置定時任務,定時自動進行緩存預熱。
小結
本文介紹了緩存雪崩產生的原因是因為短時間內大量緩存同時失效,而導致大量請求直接查詢數據庫的情況,解決方案是加鎖、隨機設置過期時間和設置二級緩存等。
還介紹了查詢數據庫無數據時會導致的每次空查詢都不走緩存的緩存穿透問題,解決方案是使用布隆過濾器和緩存空結果等。
同時還介紹了緩存在某一個高并發時刻突然失效導致的緩存擊穿問題,以及解決方案——加鎖、設置永不過期等方案,最后還介紹了優化系統性能的手段緩存預熱。































