缓存穿透 缓存击穿 缓存雪崩

Redis 高并发场景下,经常会遇到三类经典问题:

  1. 缓存穿透(Cache Penetration)
  2. 缓存击穿(Cache Breakdown/Hot Key Breakdown)
  3. 缓存雪崩(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 面试必问内容。

 
 
 
 
 
 

posted on 2026-07-05 15:11  日思日睿  阅读(12)  评论(0)    收藏  举报