网易首页 > 网易号 > 正文 申请入驻

Redis缓存穿透、击穿、雪崩终极解决方案:从底层原理到生产高可用架构落地

0
分享至

# Redis缓存穿透、击穿、雪崩终极解决方案:从底层原理到生产高可用架构落地
> 版权:凡尘版权
## 导读
做后端开发,Redis几乎是中大型系统的标配组件。很多同学本地调试、单元测试全部跑通,上线压测之后就爆出各类缓存故障。数据库压力飙升、接口大量超时、CPU打满,追查半天,根源往往就是缓存穿透、缓存击穿、缓存雪崩这三类经典问题。




网上关于这三个问题的教程很多,但大部分只讲概念,缺少生产环境可直接落地的代码,也很少讲真实线上踩过的坑。本文结合线上项目实战,从现象、底层原理、错误示范、代码实现、性能取舍完整梳理,看完可以直接用到业务开发当中。
## 一、缓存穿透:查询不存在的数据,直接打穿缓存直达数据库
### 故障现象
大量请求查询数据库根本不存在的key,缓存层拦截失效,所有流量全部落到MySQL。比如传入ID=-1、ID=99999999这类不存在业务数据,Redis查不到,每次请求都访问数据库。高并发场景下数据库CPU、连接数直接打满。
### 错误代码示例
```java
// 错误写法:没有空值处理,不存在的数据直接穿透到DB
public User getUserById(Long userId){
String cacheKey = "user:" + userId;
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
if(StringUtils.isNotEmpty(cacheValue)){
return JSON.parseObject(cacheValue,User.class);
}
//缓存没命中,直接查数据库
User user = userMapper.selectById(userId);
if(user != null){
redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(user),30, TimeUnit.MINUTES);
}
return user;
}
```
**问题分析**:当userId为不存在的id,数据库返回null,程序不会往Redis写入任何缓存。下一次相同请求过来,依旧绕过缓存查询数据库。恶意接口遍历、非法参数传入,就会造成缓存穿透攻击。
### 方案1:缓存空对象(业务简单场景首选)
查询数据库返回null,依然向Redis存入空标记,设置较短过期时间,避免相同请求反复访问数据库。
```java
public User getUserById(Long userId){
String cacheKey = "user:" + userId;
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
if(StringUtils.isNotEmpty(cacheValue)){
// 判断缓存空标记
if("NULL".equals(cacheValue)){
return null;
}
return JSON.parseObject(cacheValue,User.class);
}
User user = userMapper.selectById(userId);
if(user != null){
redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(user),30, TimeUnit.MINUTES);
}else{
// 存入空标识,短过期,避免大量无效key占满内存
redisTemplate.opsForValue().set(cacheKey,"NULL",5, TimeUnit.MINUTES);
}
return user;
}
```
> 缺点:会产生大量无效key,需要控制过期时间不能设置过长;适合并发量中等业务。
### 方案2:布隆过滤器(高并发大流量系统)
把所有合法业务ID预加载进布隆过滤器,请求进来先经过布隆过滤器拦截。如果布隆过滤器判定这个ID不存在,直接返回,不再查询Redis和数据库。
> 注意:布隆过滤器存在误判,只能过滤“一定不存在”的数据,不能100%精准匹配;不支持删除操作,业务数据大量删除场景需要额外处理。
**生产落地注意点**
1. 项目启动的时候批量把有效业务id载入布隆过滤器;
2. 新增业务数据同步写入布隆过滤器;
3. 预估业务数据总量,合理设置误判率。
## 二、缓存击穿:热点key失效,大量并发直接打数据库
### 故障现象
某一个热点高频访问的缓存key,到期瞬间,成千上万个并发请求同时来到,缓存失效,全部请求打到数据库,瞬间压垮DB。和穿透不同,**击穿针对的是某一个存在的热点数据**,比如秒杀商品、首页热门资讯。
### 错误代码
上面缓存查询示例就是典型错误,热点key过期瞬间,大量线程同时进入查询数据库逻辑。很多开发以为设置过期时间就万事大吉,线上一压测直接出事故。
### 解决方案一:互斥锁(分布式锁)
缓存失效时,只放行一个线程去查询数据库并且更新缓存,其余线程等待重试。
```java
public User getUserWithLock(Long userId){
String cacheKey = "user:" + userId;
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
if(StringUtils.isNotEmpty(cacheValue)){
return JSON.parseObject(cacheValue,User.class);
}
String lockKey = "lock:user:" + userId;
// 获取分布式锁
Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey,"1",10,TimeUnit.SECONDS);
if(!lock){
// 获取锁失败,短暂休眠后重试
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
return getUserWithLock(userId);
}
try{
//拿到锁之后,再次二次校验缓存,防止其它线程已经更新缓存
cacheValue = redisTemplate.opsForValue().get(cacheKey);
if(StringUtils.isNotEmpty(cacheValue)){
return JSON.parseObject(cacheValue,User.class);
}
User user = userMapper.selectById(userId);
if(user != null){
redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(user),30, TimeUnit.MINUTES);
}
return user;
}finally {
//释放锁
redisTemplate.delete(lockKey);
}
}
```
> 优缺点:实现简单,保证数据一致性;并发极高场景会带来少量等待,注意锁超时时间,避免死锁。
### 方案二:热点key永不过期(业务允许优先选择)
关闭Redis层面过期时间,后台开启异步线程定时刷新缓存。完全杜绝key过期带来的击穿风险。
> 适用场景:热点数据,允许短暂数据不一致;不适合强实时更新业务。
## 三、缓存雪崩:大量缓存key集中失效,Redis宕机,大规模数据库压力
### 故障现象
大批量缓存key同一时间过期,大量请求全部落到数据库;或者Redis服务整体宕机,所有缓存全部失效,数据库瞬间承载全量流量,系统整体雪崩。
⚠️区分概念:
- 击穿:**单个热点key失效**
- 雪崩:**大批量key同时失效 / Redis整体故障**
### 诱因盘点
1. 批量设置缓存过期时间完全相同,到时间集体失效;
2. Redis实例宕机、断电、网络故障;
3. Redis内存满,大量key被LRU淘汰。
### 解决方案
1. **过期时间打散,增加随机偏移量**
不要统一设置30分钟过期,在基础过期时间上叠加随机值,错开失效窗口。
```java
//基础30分钟,叠加0‑5分钟随机偏移
long expireTime = 30*60 + new Random().nextInt(5*60);
redisTemplate.opsForValue().set(key,value,expireTime,TimeUnit.SECONDS);
```
2. **Redis高可用架构**
主从 + 哨兵或者Redis‑Cluster集群,避免单点故障,Redis挂掉能自动切换。
3. **服务层限流、熔断降级**
即使缓存全部失效,通过Sentinel或者Resilience4j做接口熔断限流,数据库压力到达阈值直接返回降级提示,不让数据库被打挂。
4. **多级缓存设计**
本地Caffeine一级缓存 + Redis二级缓存,即使Redis故障,本地缓存依然可以承接一部分流量。
## 四、线上踩坑复盘,容易被忽略的细节
### 坑1:只解决穿透击穿雪崩,忽略缓存与数据库双写一致性
更新业务数据,只更新数据库,忘记更新/删除缓存,造成缓存长期脏数据。
> 最佳实践:更新数据库之后删除缓存,不要直接更新缓存;延迟双删策略处理并发读写带来的脏数据。
### 坑2:分布式锁过期时间评估错误
业务查询数据库耗时大于锁过期时间,锁提前释放,多个线程同时执行业务逻辑,锁失效。锁超时时间要大于业务预估最大执行时间。
### 坑3:布隆过滤器误判引发业务bug
业务上不要把布隆过滤器当成100%判断依据,布隆过滤器说存在,实际有可能不存在;布隆过滤器说不存在,一定不存在。
### 坑4:缓存空对象无限堆积
穿透方案存入大量空key,没有控制过期时间,Redis内存持续上涨。空对象过期时间必须设置很短,5‑10分钟区间。
## 五、生产选型参考表
|问题类型|推荐方案|适用场景|局限|
|---|---|---|---|
|缓存穿透|缓存空值|中小并发业务|产生无效key|
|缓存穿透|布隆过滤器|大流量高并发系统|无法删除、存在误判|
|缓存击穿|分布式锁|追求数据一致性|存在少量等待开销|
|缓存击穿|热点key永不过期|允许短暂数据不一致|需要定时刷新任务|
|缓存雪崩|过期时间随机打散|绝大多数业务|无法解决Redis宕机|
|缓存雪崩|集群+限流熔断|核心生产环境|架构复杂度上升|
## 六、开发编码规范总结
1. 所有带缓存的查询接口,必须思考:如果缓存全部失效,数据库能不能扛住流量;提前做好降级兜底。
2. 热点业务key,优先考虑永不过期,后台异步刷新。
3. 批量写入缓存,一定要增加随机过期偏移,杜绝集体过期。
4. 分布式锁必须设置超时时间,finally代码块释放锁,防止死锁。
5. 线上上线前,做缓存失效场景压测,模拟key大批量过期,观察数据库指标。
很多时候线上故障不是代码逻辑写错,而是没有考虑缓存失效、并发竞争这种边界场景。本地单线程调试完全正常,一旦放到高并发生产环境,各种隐藏问题全部暴露出来。理解底层原理,结合业务场景选型,不要网上复制一段代码直接就上线。
> 版权:凡尘版权

声明:个人原创,仅供参考

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
死伤惨重!德国死亡人数过万,欧洲死亡累积超2.5万人

死伤惨重!德国死亡人数过万,欧洲死亡累积超2.5万人

果妈聊娱乐
2026-08-11 18:45:08
老戏骨杨昆两夺金鹰奖,却因4.5万物业费被告,晚年带货维持生计

老戏骨杨昆两夺金鹰奖,却因4.5万物业费被告,晚年带货维持生计

娱说瑜悦
2026-08-12 14:54:46
预售订单破10万,首月零售仅4661台!比亚迪大唐翻车了?

预售订单破10万,首月零售仅4661台!比亚迪大唐翻车了?

玩车专家1
2026-08-12 07:58:54
年薪221万、万亿央企上海子公司原总裁,卸任后突然被查

年薪221万、万亿央企上海子公司原总裁,卸任后突然被查

湘财Plus
2026-08-12 12:21:57
巴沙尔·阿萨德,被判死刑

巴沙尔·阿萨德,被判死刑

南方都市报
2026-08-11 17:59:32
诺奖委,是时候给黄院士颁奖了!本人首次回应来了!

诺奖委,是时候给黄院士颁奖了!本人首次回应来了!

闲侃闲侃
2026-08-12 08:08:33
哪吒获奖无人站起,惊蛰无声获奖全体起立,讽刺的是哪吒的奖更大

哪吒获奖无人站起,惊蛰无声获奖全体起立,讽刺的是哪吒的奖更大

芊手若
2026-08-11 11:13:13
再爆大冷2-6!吴宜泽、赵心童都出局了,世界第1最新争夺形势出炉

再爆大冷2-6!吴宜泽、赵心童都出局了,世界第1最新争夺形势出炉

小火箭爱体育
2026-08-12 17:05:35
实话实说!没有2米26的张子宇,中国女篮就显得不会得分

实话实说!没有2米26的张子宇,中国女篮就显得不会得分

体育哲人
2026-08-12 19:55:53
1969年肖全夫拒绝向苏军坦克开火!陈锡联火了:我的命令都不听?

1969年肖全夫拒绝向苏军坦克开火!陈锡联火了:我的命令都不听?

兴趣知识
2026-08-10 16:55:02
36氪:姆巴佩即将签约李宁,并出资持有相关股份

36氪:姆巴佩即将签约李宁,并出资持有相关股份

懂球帝
2026-08-12 15:59:08
辽篮名宿郑永刚,退役成名帅,56岁财富自由,女儿1米8很漂亮

辽篮名宿郑永刚,退役成名帅,56岁财富自由,女儿1米8很漂亮

大西体育
2026-08-12 15:37:04
认栽了,印尼彻底服软了!

认栽了,印尼彻底服软了!

有态度的何总
2026-08-12 13:30:29
为什么台湾是地球上最“易守难攻”的岛屿?

为什么台湾是地球上最“易守难攻”的岛屿?

野书文
2026-08-11 20:48:08
不查不知道!原来卫诗雅身上这条亮眼的蓝裙子,只是一件27000的成衣,英皇根本没有给她借到高定礼服

不查不知道!原来卫诗雅身上这条亮眼的蓝裙子,只是一件27000的成衣,英皇根本没有给她借到高定礼服

火山詩话
2026-08-12 08:15:28
俄国认为:《尼布楚条约》是耻辱,黑龙江流域自古就是他们的领土

俄国认为:《尼布楚条约》是耻辱,黑龙江流域自古就是他们的领土

浔阳咸鱼
2026-08-11 17:15:07
4 家医院拒诊!腹痛患儿连跑 5 家医院!一场医疗事件的影响有多大?这个国家一代儿科医生出走!儿科年年亏损,医疗服务提价有用么?

4 家医院拒诊!腹痛患儿连跑 5 家医院!一场医疗事件的影响有多大?这个国家一代儿科医生出走!儿科年年亏损,医疗服务提价有用么?

梅斯医学
2026-08-11 07:53:23
“当地人都知道干死了五个人”,一网友在涉西平“7·30”命案短视频留言区造谣被罚!河南网警通报

“当地人都知道干死了五个人”,一网友在涉西平“7·30”命案短视频留言区造谣被罚!河南网警通报

扬子晚报
2026-08-11 15:27:35
日本啤酒上的辛口是什么意思?

日本啤酒上的辛口是什么意思?

日本通
2026-08-11 15:06:27
完蛋,楼市这次又降了!

完蛋,楼市这次又降了!

金刀说房
2026-08-12 17:35:09
2026-08-12 21:12:49
情字白头369
情字白头369
个人生活中所拍摄的照片以及成长过程中的心得感悟
15文章数 1关注度
往期回顾 全部

科技要闻

Manus第二季,浪子回头

头条要闻

外卖小哥称"我人生最后一天啦" 女顾客收信息果断报警

头条要闻

外卖小哥称"我人生最后一天啦" 女顾客收信息果断报警

体育要闻

杜兰特,所谓的“联盟小王”

娱乐要闻

老戏骨杨昆两夺金鹰奖,因物业费被告

财经要闻

李嘉诚,再一次大撤退!他嗅到了什么?

汽车要闻

这台大众不一般 上汽大众ID.ERA.5S综合续航约2000公里

态度原创

教育
旅游
亲子
时尚
军事航空

教育要闻

2026年中国211高校出炉!哈工程跃升到第5,安徽大学排名33

旅游要闻

实探!从“点灯没油”到年旅游收入13亿元:袁家村何以成为乡村旅游第一村?

亲子要闻

艾莉一眨眼要过两岁生日了,最近中英文都进步挺快,带孩子去买点装饰布置下,明天给三宝庆生!

赫本喜欢的维希格,早秋穿正合适

军事要闻

通过“毕业大考” 湖北舰“持证上岗”

无障碍浏览 进入关怀版