有一天凌晨 2 点,监控突然像过年放鞭炮一样,全红。QPS 飙升、数据库 CPU 100%、连接池打满、报警电话响到我怀疑人生。同事迷迷糊糊地问我一句:
“是不是缓存雪崩了?”
那一刻,我才真正意识到:
Redis 不只是快,它也可能是一场“雪灾”。
从一个真实又残酷的故事说起
想象一个场景。你在北方某城市生活,这座城市冬天靠集中供暖。平时大家都很舒服,屋里 25 度,外面零下十几度也不慌。
某天,供暖公司为了方便管理,统一设置:凌晨 3 点检修。结果会发生什么?
- 3:00 整座城市同时断暖
- 千家万户瞬间降温
- 大家同时打开电暖气
- 小区电闸直接拉闸
- 整个系统彻底瘫痪
你是不是已经开始瑟瑟发抖了?
Redis 缓存雪崩,本质上就是这一幕
缓存 = 供暖系统数据库 = 电网 / 发电站请求 = 家庭用电
当大量缓存在同一时间失效,所有请求瞬间打到数据库,数据库扛不住,就直接“崩”。
面试官口中的标准定义
面试时你可以先给一个非常干净、标准的定义:
缓存雪崩:指在某一时间点,大量缓存数据同时失效,导致原本由缓存承担的请求全部落到数据库,数据库在短时间内承受巨大压力,最终可能宕机。
如果你能补一句:
“这是高并发系统中非常典型的一类级联故障。”
面试官一般会点头。
缓存雪崩是怎么“雪”起来的?
1、最常见的作死操作:统一过期时间
很多系统,刚开始写缓存代码时是这样的:
看起来很合理,对吧?但如果你是批量加载缓存呢?
然后悲剧来了
- 00:00 批量加载缓存
- 00:30 所有缓存同时过期
- 00:30:01 所有请求直冲数据库
这不是雪崩,这是定时炸弹。
2、缓存节点整体不可用(进阶版雪崩)
除了过期时间问题,还有一种更狠的情况:
- Redis 宕机
- 网络抖动
- 主从切换失败
这时:不是“缓存失效”,而是“缓存消失”, 所有请求,毫无缓冲,直接怼数据库。
雪崩发生时,系统会长什么样?
我们用一张表来直观感受一下:
你会发现一个事实:缓存雪崩从来不是“缓存的问题”,而是“数据库被拖下水”的问题。
解决方案一:缓存过期时间加随机值(最基础)
不要让整座城市同一秒断暖,而是:
- 这栋楼 2:55
- 那栋楼 3:03
- 再远一点 3:17
分批降温,电网就能扛住。
1、正确的缓存姿势
这样做的效果:
- 缓存不会同一时间失效
- 数据库流量被“摊平”
- 成本低、实现简单
2、适用场景
解决方案二:加锁排队(并发不高时最常用)
这招,在面试和实际项目里都非常常见。
先说核心思想:缓存失效时,只允许一个线程去查数据库,其它线程等着。
就像:
- 一个窗口卖票
- 排队买
- 不能一拥而上把窗口拆了
1、伪代码逻辑
2、执行过程拆解
3、优点与缺点
解决方案三:缓存标记位(进阶但非常优雅)
这是我非常喜欢的一种方案,因为它——像一个成熟的系统工程师思路。
1、先来个生活类比
你去便利店买盒饭,发现:
- 盒饭在
- 但贴着一个标签: “待补货”
你会怎么做?
- 你不会冲进后厨自己做
- 你会等店员补货
2、技术层面的思路
缓存中不仅存数据,还存“状态”, 状态包括:
- 是否过期
- 是否正在重建
3、数据结构设计
3、核心代码示意
异步重建缓存
4、这种方案的特点
三种方案对比表格
面试时怎么一口气说漂亮?
如果面试官问你:
“Redis 缓存雪崩怎么解决?”
你可以这样回答:
缓存雪崩主要是由于大量缓存同一时间失效导致的。
常见解决方案包括:
第一,给缓存过期时间增加随机值,避免集中失效;
第二,在并发量不高的情况下,可以通过加锁控制只有一个线程重建缓存;
第三,更成熟的做法是引入缓存标记位或逻辑过期,缓存过期后先返回旧值,再异步更新,避免请求直接打到数据库。
这一段,说完,基本稳了。
最后聊一句掏心窝子的
缓存不是银弹。
- 它能救你
- 也能埋你
真正成熟的系统,从来不是“用了 Redis 就万事大吉”,而是:你是否为“缓存失效的那一刻”做好了准备。
END
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
我们,下篇见。