有一天凌晨 2 點,監控突然像過年放鞭炮一樣,全紅。QPS 飆升、數據庫 CPU 100%、連接池打滿、報警電話響到我懷疑人生。同事迷迷糊糊地問我一句:

“是不是緩存雪崩了?”

那一刻,我才真正意識到:

Redis 不只是快,它也可能是一場“雪災”。

從一個真實又殘酷的故事説起

想象一個場景。你在北方某城市生活,這座城市冬天靠集中供暖。平時大家都很舒服,屋裏 25 度,外面零下十幾度也不慌。

某天,供暖公司為了方便管理,統一設置:凌晨 3 點檢修。結果會發生什麼?

  • 3:00 整座城市同時斷暖
  • 千家萬户瞬間降温
  • 大家同時打開電暖氣
  • 小區電閘直接拉閘
  • 整個系統徹底癱瘓

你是不是已經開始瑟瑟發抖了?

Redis 緩存雪崩,本質上就是這一幕

緩存 = 供暖系統數據庫 = 電網 / 發電站請求 = 家庭用電

大量緩存在同一時間失效所有請求瞬間打到數據庫,數據庫扛不住,就直接“崩”。

面試官口中的標準定義

面試時你可以先給一個非常乾淨、標準的定義

緩存雪崩:指在某一時間點,大量緩存數據同時失效,導致原本由緩存承擔的請求全部落到數據庫,數據庫在短時間內承受巨大壓力,最終可能宕機。

如果你能補一句:

“這是高併發系統中非常典型的一類級聯故障。”

面試官一般會點頭。

緩存雪崩是怎麼“雪”起來的?

1、最常見的作死操作:統一過期時間

很多系統,剛開始寫緩存代碼時是這樣的:

面試必問:Redis 緩存雪崩,別再只會背定義了_緩存

看起來很合理,對吧?但如果你是批量加載緩存呢?

面試必問:Redis 緩存雪崩,別再只會背定義了_Redis_02

然後悲劇來了

  • 00:00 批量加載緩存
  • 00:30 所有緩存同時過期
  • 00:30:01 所有請求直衝數據庫

這不是雪崩,這是定時炸彈

2、緩存節點整體不可用(進階版雪崩)

除了過期時間問題,還有一種更狠的情況:

  • Redis 宕機
  • 網絡抖動
  • 主從切換失敗

這時:不是“緩存失效”,而是“緩存消失”,所有請求,毫無緩衝,直接懟數據庫。

雪崩發生時,系統會長什麼樣?

我們用一張表來直觀感受一下:

面試必問:Redis 緩存雪崩,別再只會背定義了_Redis_03

你會發現一個事實:緩存雪崩從來不是“緩存的問題”,而是“數據庫被拖下水”的問題。

解決方案一:緩存過期時間加隨機值(最基礎)

不要讓整座城市同一秒斷暖,而是:

  • 這棟樓 2:55
  • 那棟樓 3:03
  • 再遠一點 3:17

分批降温,電網就能扛住。

1、正確的緩存姿勢

面試必問:Redis 緩存雪崩,別再只會背定義了_Redis_04

這樣做的效果:

  • 緩存不會同一時間失效
  • 數據庫流量被“攤平”
  • 成本低、實現簡單

2、適用場景

面試必問:Redis 緩存雪崩,別再只會背定義了_Redis_05

解決方案二:加鎖排隊(併發不高時最常用)

這招,在面試和實際項目裏都非常常見

先説核心思想:緩存失效時,只允許一個線程去查數據庫,其它線程等着。

就像:

  • 一個窗口賣票
  • 排隊買
  • 不能一擁而上把窗口拆了

1、偽代碼邏輯

面試必問:Redis 緩存雪崩,別再只會背定義了_Redis_06

2、執行過程拆解

面試必問:Redis 緩存雪崩,別再只會背定義了_緩存_07

3、優點與缺點

面試必問:Redis 緩存雪崩,別再只會背定義了_緩存_08

解決方案三:緩存標記位(進階但非常優雅)

這是我非常喜歡的一種方案,因為它——像一個成熟的系統工程師思路

1、先來個生活類比

你去便利店買盒飯,發現:

  • 盒飯在
  • 但貼着一個標籤:“待補貨”

你會怎麼做?

  • 你不會衝進後廚自己做
  • 你會等店員補貨

2、技術層面的思路

緩存中不僅存數據,還存“狀態”,狀態包括:

  • 是否過期
  • 是否正在重建

3、數據結構設計

面試必問:Redis 緩存雪崩,別再只會背定義了_緩存_09

3、核心代碼示意

面試必問:Redis 緩存雪崩,別再只會背定義了_緩存_10

異步重建緩存

面試必問:Redis 緩存雪崩,別再只會背定義了_數據庫_11

4、這種方案的特點

面試必問:Redis 緩存雪崩,別再只會背定義了_Redis_12

三種方案對比表格

面試必問:Redis 緩存雪崩,別再只會背定義了_緩存_13

面試時怎麼一口氣説漂亮?

如果面試官問你:

“Redis 緩存雪崩怎麼解決?”

你可以這樣回答:

緩存雪崩主要是由於大量緩存同一時間失效導致的。

常見解決方案包括:

第一,給緩存過期時間增加隨機值,避免集中失效;

第二,在併發量不高的情況下,可以通過加鎖控制只有一個線程重建緩存;

第三,更成熟的做法是引入緩存標記位或邏輯過期,緩存過期後先返回舊值,再異步更新,避免請求直接打到數據庫。

這一段,説完,基本穩了

最後聊一句掏心窩子的

緩存不是銀彈。

  • 它能救你
  • 也能埋你

真正成熟的系統,從來不是“用了 Redis 就萬事大吉”,而是:你是否為“緩存失效的那一刻”做好了準備。

END

我是小米,一個喜歡分享技術的31歲程序員。如果你喜歡我的文章,歡迎關注我的微信公眾號“軟件求生”,獲取更多技術乾貨!

我們,下篇見。