Redis 缓存雪崩、击穿、穿透 是缓存使用中最经典的三大“灾难级”问题,几乎所有中大型系统都会遇到,也几乎是面试中 Redis 相关问题出现频率 Top 3 的概念。
下面用最通俗、最容易记住的方式给你讲清楚三者的定义、成因、危害、区别、解决方案。
一张表先看清三者的本质区别
| 问题名称 | 什么时候发生 | 核心原因 | 影响范围 | 表现形式 | 典型场景举例 |
|---|---|---|---|---|---|
| 缓存穿透 | 查询一个根本不存在的数据 | 请求直接打到数据库 | 单个 key 反复打穿 | 数据库压力瞬间暴增 | 黑客猜用户 ID、恶意爬虫刷不存在的商品 ID |
| 缓存击穿 | 某个热点 key 突然过期 | 大量并发同时请求同一个已过期 key | 单个热点 key | 瞬间把数据库打崩 | 秒杀商品、热门文章、直播间人数 |
| 缓存雪崩 | 大量 key 在同一时间点集体失效 | 大量缓存同时过期 + 冷启动 | 几乎所有 key | 数据库像雪崩一样瞬间被打垮 | 双11零点、促销活动统一设置了相同过期时间 |
详细解释 + 生活化比喻
1. 缓存穿透(Cache Penetration)
比喻:
你去一个从没开过门的店问“有没有黄金项链”,老板每次都要去仓库翻一遍告诉你“没有”,你一天问 10 万次,老板累死了。
成因:
- 查询一个数据库里压根不存在的数据
- 缓存里自然也没有 → 每次都穿透到数据库
最危险的几种情况:
- 恶意攻击(枚举不存在的 ID)
- 前端传了非法参数(负数 ID、空字符串、超大值)
- 数据库里数据被删除了,但缓存没清理
解决方案(按优先级排序):
- 缓存空结果(最常用)
if (redis.get(key) == null) {
Object obj = db.query(id);
if (obj == null) {
redis.setex(key, 60, "EMPTY"); // 缓存空值,短过期时间
} else {
redis.setex(key, 3600, obj);
}
}
- 布隆过滤器(适合黑客攻击场景)
- 在缓存前加一层布隆过滤器,提前判断 key 肯定不存在就直接返回
- 接口参数校验 + 限流 + IP 黑名单
2. 缓存击穿(Cache Breakdown / Hot Key 失效)
比喻:
商场里最火的那件限量款羽绒服突然卖完了(缓存过期),门口瞬间挤了几千人同时问“还有货吗”,全都冲到仓库去问老板。
成因:
- 一个极热的 key(QPS 几千上万)
- 恰好在这个时间点过期
- 大量请求同时穿透到数据库
解决方案(目前主流做法):
- 永不过期 + 逻辑过期(最推荐)
// 缓存里存两个字段:data + expireTime
if (now > expireTime) {
// 异步线程去刷新
asyncRefresh(key);
return oldData; // 先返回旧数据
}
- 加分布式锁(setnx / Redisson lock)
- 只有一个请求去查数据库,其他请求等待或返回旧数据
if (lock.tryLock(10, TimeUnit.SECONDS)) {
try {
// 双重检查
if (redis.get(key) == null) {
redis.setex(key, 3600, db.query());
}
} finally {
lock.unlock();
}
}
- 热点 key 预加载 / 随机偏移过期时间
3. 缓存雪崩(Cache Avalanche)
比喻:
商场里 80% 的商品同时到保质期,全都下架了,几万人同时冲向仓库,仓库直接被挤爆。
成因:
- 大量 key 在同一时间点过期
- 或者缓存服务器宕机 / 重启 / 冷启动
解决方案(多管齐下):
- 过期时间加随机值
redis.setex(key, 3600 + random(300, 600), value); // 1小时 ± 几分钟
- 多级缓存(本地缓存 + Redis)
- Caffeine / Guava Cache 做一级,本地命中就不打 Redis
- 热点数据永不过期 + 定时任务后台刷新
- Redis 集群 + 主从 + 哨兵,避免单点宕机
- 熔断 / 降级 / 限流(Hystrix、Resilience4j、Sentinel)
- 缓存预热(启动时把热点数据提前加载)
三者对比总结口诀(面试背这个就够)
- 穿透:查根本不存在的东西 → 缓存空对象 / 布隆过滤器
- 击穿:一个超级热点突然没了 → 分布式锁 / 逻辑过期 / 永不过期
- 雪崩:一大堆 key同时没了 → 随机过期 + 多级缓存 + 预热
一句话记住:
穿透是“查鬼”,击穿是“抢购疯抢一件”,雪崩是“商场同时清仓”。
你现在项目里有没有遇到过这三个问题中的哪一个?
或者想看某个具体解决方案的代码示例(比如分布式锁防击穿、布隆过滤器防穿透)?可以继续问~