Redis 缓存穿透、击穿、雪崩,一次讲清!

在 Java 后端开发中,Redis 缓存是非常常见的性能优化手段。但缓存并不是加上就万事大吉。当大量请求无法有效命中缓存时,数据库仍然可能承受巨大的压力。

缓存穿透、缓存击穿、缓存雪崩,就是 Redis 中三个经典的缓存问题。它们名字相似,但发生原因和解决方案并不相同。

本文从实际业务场景出发,一次讲清这三个问题。

一、先理解正常的缓存查询流程

正常情况下,查询数据的流程如下:

  1. 接收用户请求。

  2. 查询 Redis 缓存。

  3. 如果缓存命中,直接返回结果。

  4. 如果缓存未命中,查询数据库。

  5. 将查询结果写入缓存,再返回结果。

缓存的核心作用,是减少数据库访问次数、降低响应延迟。

而缓存穿透、击穿和雪崩,都会在特定情况下让大量请求绕过缓存,给数据库带来额外压力。

二、缓存穿透:查询的数据根本不存在

1. 什么是缓存穿透?

假设系统提供商品详情接口,用户不断请求一个不存在的商品 ID。

例如:

  • Redis 中没有这个商品。

  • 查询数据库后,发现商品也不存在。

  • 请求结束,但缓存中依然没有有效的商品数据。

下一次相同请求到来时,流程又会重复。

如果大量请求持续查询不存在的数据,数据库就可能承受不必要的压力。

核心原因:请求查询的数据不存在,缓存无法通过正常的数据回填解决这个问题。

2. 如何解决缓存穿透?

方案一:缓存空值

查询数据库确认数据不存在后,将一个空结果缓存一段较短的时间。

这样,相同的无效请求再次到来时,就可以直接从缓存中得到结果,而不必反复查询数据库。

注意:需要设置合理的过期时间,避免无效 Key 长期占用缓存空间。

方案二:布隆过滤器

布隆过滤器可以用于判断某个元素是否可能存在于集合中。

查询前先进行判断:

  • 明确不存在:直接拦截请求。

  • 可能存在:继续查询 Redis 或数据库。

需要注意,布隆过滤器可能产生误判,将实际不存在的元素判断为"可能存在",但不会因此直接确认数据一定存在。

方案三:参数校验与限流

对商品 ID、参数格式和取值范围进行校验,并对异常请求实施限流或风控。

实际项目中,可以组合使用以上方案。

三、缓存击穿:热点 Key 突然失效

1. 什么是缓存击穿?

假设某个热门商品正在参加秒杀活动。

平时,大量请求都能命中 Redis。但在某个瞬间,这个热点 Key 过期了。

此时,如果大量请求同时到来,它们可能一起发现缓存未命中,然后并发查询数据库。

最终,一个热点 Key 的失效,就可能让数据库承受大量重复查询。

核心原因:某个热点缓存 Key 失效,导致大量并发请求同时回源。

2. 如何解决缓存击穿?

方案一:互斥锁

通过互斥锁或分布式锁控制缓存重建过程。

只有获得锁的请求负责查询数据库并重建缓存,其他请求等待、重试或进入降级逻辑。

需要注意锁超时、异常释放和等待时间等问题,避免引入新的故障。

方案二:逻辑过期

缓存数据中额外记录一个逻辑过期时间。

数据达到逻辑过期时间后,不立即删除缓存,而是由后台任务异步刷新数据。

这样,部分请求仍然可以读取旧数据,避免所有请求同时回源。

这种方案适合允许短时间返回旧数据的业务。

方案三:热点预热

提前将热点数据加载到缓存中,减少冷启动时的缓存未命中。

对于需要长期保持可用的热点数据,也可以采用主动刷新机制,而不是完全依赖普通 TTL 过期。

四、缓存雪崩:大量缓存同时失效

1. 什么是缓存雪崩?

缓存雪崩通常有两类场景。

场景一:大量 Key 同时过期

例如,系统批量写入大量缓存,并为它们设置了相同的过期时间。

到期时,大量 Key 集中失效,导致请求同时回源数据库。

场景二:Redis 服务不可用

如果 Redis 出现宕机、网络故障等问题,大量请求可能无法正常使用缓存,进而给数据库带来巨大压力。

如果数据库也无法承受这些请求,就可能进一步造成服务响应缓慢甚至不可用。

核心原因:大量缓存同时失效,或者缓存整体不可用,导致大规模请求集中回源。

2. 如何解决缓存雪崩?

方案一:给 TTL 增加随机值

不要让大量缓存使用完全相同的过期时间。

例如,在基础 TTL 上增加一个随机时间偏移,让不同 Key 分散过期,减少集中失效的风险。

方案二:保障 Redis 高可用

根据业务需求设计合理的高可用架构,同时配置监控、告警和故障处理机制。

方案三:限流、熔断与降级

当缓存故障导致回源压力增大时,通过限流控制请求规模,通过熔断避免持续调用故障服务,并对非核心业务实施降级。

方案四:控制数据库回源压力

可以结合请求合并、热点保护、合理的数据库连接池配置和容量评估,降低故障期间的数据库压力。

需要强调的是,高可用不能保证系统永不故障,真正重要的是故障发生时,系统仍然能够控制影响范围。

五、三大缓存问题对比

对比维度 缓存穿透 缓存击穿 缓存雪崩
发生原因 数据不存在 热点 Key 失效 大量 Key 同时失效或 Redis 故障
影响特点 无效请求持续查库 热点数据并发回源 大量请求集中回源
典型场景 恶意构造不存在的 ID 秒杀商品缓存过期 批量缓存同时过期
常见方案 参数校验、空值缓存、布隆过滤器 互斥锁、逻辑过期、热点预热 随机 TTL、高可用、限流降级

记忆口诀:

  • 缓存穿透:查不到。

  • 缓存击穿:热点倒。

  • 缓存雪崩:一大片。

六、最后总结

Redis 缓存问题不能只靠某一个功能解决,需要结合实际业务场景进行设计。

  • 穿透:重点是拦截无效请求。

  • 击穿:重点是控制热点缓存重建时的并发。

  • 雪崩:重点是分散缓存失效,并做好故障兜底。

理解问题的发生原因,比单纯记忆解决方案更重要。

我是一个持续学习、持续实践的 Java 后端开发者,也会持续分享后端基础、项目实践和 AI 应用开发相关内容。

如果这篇文章对你有帮助,欢迎点赞、收藏,也欢迎在评论区交流你在项目中遇到的缓存问题。

相关推荐
梓豪csd1 小时前
消息订阅中心:谁该收到哪条通知
java·开发语言·人工智能·低代码·自动化·1024程序员节
秋饼1 小时前
Spring AI 2.1 Message Parts 深度实战:推理签名回放与跨轮记忆持久化的源码级落地
java·ai·技术分享·后端开发
thefool1122662 小时前
TCP 1
java·1024程序员节
paopaokaka_luck4 小时前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
金銀銅鐵5 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(下)
java·后端·ai编程
AI人工智能+电脑小能手5 小时前
一次请求打几个下游,这个Java并行任务工具类6个方法按序聚合
java·线程池·并发编程·1024程序员节·批量处理·并行任务
paopaokaka_luck7 小时前
心理咨询管理系统(WebSocket实时沟通、基于用户的协同过滤、 ECharts多维分析、 预约业务流程、预约校验与定时处理)
java·vue.js·spring boot·mysql·spring·echarts·1024程序员节
2301_32241428047 小时前
springboot农产品交易平台的设计与实现08055-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·spring boot·redis·mysql·tomcat·maven·mybatis
乐茵lin7 小时前
Redis 持久化机制详解:RDB 与 AOF
redis·bootstrap·mybatis