缓存穿透 缓存击穿 缓存雪崩
Redis 高并发场景下,经常会遇到三类经典问题:
- 缓存穿透(Cache Penetration)
- 缓存击穿(Cache Breakdown/Hot Key Breakdown)
- 缓存雪崩(Cache Avalanche)
很多人容易混淆,实际上它们产生原因和解决方案完全不同。
1. 缓存穿透
什么是缓存穿透?
查询一个根本不存在的数据。
请求流程:
用户请求
↓
Redis不存在
↓
查询MySQL
↓
MySQL也不存在
↓
返回空
例如:
用户ID:
1~100000
攻击者不断请求:
userId=999999999
流程:
Redis Miss
↓
MySQL Miss
↓
Redis Miss
↓
MySQL Miss
每次都会打到数据库。
危害
假设:
QPS = 10000
全部查询不存在的数据:
Redis全部失效
MySQL被直接打爆
解决方案
方案1:缓存空对象(最常用)
数据库查不到时:
User user = userMapper.selectById(id);
if(user == null){
redis.set(key, "NULL", 5分钟);
}
下次查询:
Redis命中NULL
直接返回
方案2:布隆过滤器
Redis前增加:
BloomFilter
结构:
存在:
1
2
3
4
5
请求:
10000000
先查布隆过滤器:
不存在
直接返回:
无需访问Redis
无需访问MySQL
方案3:参数校验
例如:
if(id <= 0){
return;
}
或者:
手机号格式校验
订单号格式校验
提前拦截非法请求。
2. 缓存击穿
什么是缓存击穿?
某个热点 Key突然失效。
大量请求同时访问数据库。
例如:
商品详情:
product:1001
这个商品非常热门:
QPS = 50000
缓存过期:
product:1001 失效
瞬间:
50000请求
↓
全部访问MySQL
数据库压力暴增。
示意图
请求
↓
Redis Miss
↓
50000线程同时查数据库
↓
数据库崩溃
危害
热点数据:
明星直播商品
热门游戏房间
首页推荐
最容易发生。
解决方案
方案1:互斥锁(最常见)
只有一个线程查询数据库。
if(redis.get(key)==null){
if(lock.tryLock()){
// 查询数据库
// 回写缓存
}else{
Thread.sleep(50);
// 重试
}
}
流程:
线程1
↓
拿锁成功
↓
查数据库
线程2~50000
↓
等待
方案2:逻辑过期
缓存永不过期:
{
"data": {...},
"expireTime":"2026-06-02 12:00:00"
}
读取:
过期
↓
返回旧数据
↓
后台异步刷新
用户无感知。
方案3:热点数据永不过期
例如:
商品分类
地区编码
系统配置
直接:
不设置TTL
3. 缓存雪崩
什么是缓存雪崩?
大量缓存同时失效。
例如:
100万个Key
统一设置:
TTL = 30分钟
30分钟后:
全部过期
大量请求直接访问数据库。
示意图
100万Key
↓
同时过期
↓
Redis全部Miss
↓
MySQL暴涨
↓
数据库崩溃
另一种雪崩
Redis整个实例宕机:
Redis Down
所有请求:
直接打数据库
也是缓存雪崩。
危害
比缓存击穿更严重。
因为:
击穿:
一个热点Key失效
雪崩:
大量Key失效
解决方案
方案1:TTL随机化
不要:
redis.set(key,value,1800);
改成:
redis.set(
key,
value,
1800 + RandomUtils.nextInt(0,300)
);
结果:
30分
31分
32分
34分
分散过期时间。
方案2:多级缓存
浏览器缓存
↓
Nginx缓存
↓
Redis缓存
↓
MySQL
减少数据库压力。
方案3:Redis集群
例如:
- Redis Sentinel
- Redis Cluster
避免单点故障。
方案4:服务降级与限流
使用:
- Sentinel
- Hystrix(老方案)
- Resilience4j
例如:
@RateLimiter
数据库压力过大时:
返回默认值
返回缓存旧值
直接限流
三者对比
| 问题 | 原因 | 访问数据库情况 | 典型场景 |
|---|---|---|---|
| 缓存穿透 | 查询不存在数据 | 每次都访问 | 恶意攻击、非法ID |
| 缓存击穿 | 热点Key过期 | 瞬间大量访问 | 热门商品、热点房间 |
| 缓存雪崩 | 大量Key同时失效或Redis宕机 | 大量访问 | 系统级故障 |
一句话记忆
缓存穿透
查不到的数据
Redis没有
数据库也没有
解决:
空值缓存
布隆过滤器
缓存击穿
一个热点Key失效
大量请求同时打数据库
解决:
互斥锁
逻辑过期
热点永不过期
缓存雪崩
大量Key同时失效
或者Redis整体宕机
解决:
随机过期时间
Redis高可用
限流降级
多级缓存
面试中最经典的记忆方式是:
穿透:查不到
击穿:热点失效
雪崩:大量失效
这三个问题几乎是 Redis 面试必问内容。
浙公网安备 33010602011771号