在 Java 后端开发中,Redis 缓存是非常常见的性能优化手段。但缓存并不是加上就万事大吉。当大量请求无法有效命中缓存时,数据库仍然可能承受巨大的压力。
缓存穿透、缓存击穿、缓存雪崩,就是 Redis 中三个经典的缓存问题。它们名字相似,但发生原因和解决方案并不相同。
本文从实际业务场景出发,一次讲清这三个问题。
一、先理解正常的缓存查询流程
正常情况下,查询数据的流程如下:
-
接收用户请求。
-
查询 Redis 缓存。
-
如果缓存命中,直接返回结果。
-
如果缓存未命中,查询数据库。
-
将查询结果写入缓存,再返回结果。
缓存的核心作用,是减少数据库访问次数、降低响应延迟。
而缓存穿透、击穿和雪崩,都会在特定情况下让大量请求绕过缓存,给数据库带来额外压力。

二、缓存穿透:查询的数据根本不存在
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 应用开发相关内容。
如果这篇文章对你有帮助,欢迎点赞、收藏,也欢迎在评论区交流你在项目中遇到的缓存问题。