缓存击穿、穿透、雪崩必须分而治之:击穿用互斥锁或逻辑过期防热点失效洪峰;穿透靠参数校验、空值缓存和布隆过滤器拦截非法请求;雪崩通过TTL随机抖动、多级缓存和熔断限流避免集体失效。

Java 项目集成 Redis 时,缓存击穿、穿透、雪崩不是“会不会发生”的问题,而是“什么时候发生”的问题。三者成因不同,必须分而治之——靠单一手段(比如只加过期时间或只设空值)无法兜底。
防缓存击穿:热点 Key 失效时别让所有请求冲垮 DB
击穿本质是单个高热 Key 过期瞬间的并发洪峰。比如首页 Banner、秒杀商品详情页,QPS 可达上万,一旦失效,几十个线程同时查库写缓存,数据库立刻吃紧。
-
互斥锁(推荐首选):用 Redis 的
SET key value NX PX 5000实现分布式锁,只放行一个线程查库并回填缓存,其余线程等待后直接读缓存。Spring Boot 中可用RedisTemplate.opsForValue().setIfAbsent()+ 自旋重试,避免死锁。 -
逻辑过期(更优):缓存值中嵌入一个“逻辑过期时间”字段(如 JSON 中加
"expireAt": 1722226800000),不依赖 Redis TTL。请求时先判断逻辑时间是否过期;若过期,用互斥锁异步刷新,当前请求仍返回旧数据,平滑过渡。 - 永不过期 + 主动刷新:对极核心且变更不频繁的数据(如城市列表、配置项),设为永不过期,后台定时任务(如每 30 分钟)拉取最新数据更新缓存。
防缓存穿透:不存在的 Key 别让它碰数据库一下
穿透是恶意或异常请求查根本不存在的数据(如 ID=-1、超大随机数),缓存和 DB 都无记录,每次都要白跑一趟,攻击量大时 DB 连接池直接耗尽。
- 参数校验前置:Controller 层就拦截明显非法值——ID ≤ 0、字符串长度超限、格式不符(如手机号非 11 位),直接返回 400,不进 service。
-
缓存空值(简单有效):DB 查无结果时,往 Redis 存一个带短过期(如 2~5 分钟)的占位符(建议用自定义对象如
NullValue,避免与真实null混淆)。 -
布隆过滤器(高并发兜底):初始化时将所有合法 ID 加入布隆过滤器(Guava 或 RedisBloom)。请求来时先
bloomFilter.mightContain(id),返回 false 就直接拒绝,不查缓存也不查库。注意它有误判率(可设 ≤0.1%),但绝不会漏判。
防缓存雪崩:别让大批缓存在同一秒集体消失
雪崩分两种:一是大量 Key 同一时刻过期(如批量导入时统一设了 2 小时 TTL),二是 Redis 整体宕机。前者是设计失误,后者是架构短板。
立即学习“Java免费学习笔记(深入)”;
-
过期时间加随机抖动:设置 TTL 时叠加随机值,例如基准 3600 秒,实际设为
3600 + new Random().nextInt(300)(即 ±5 分钟),打散失效峰值。 - 多级缓存降级:本地缓存(Caffeine)+ Redis 组合。Redis 不可用时,Caffeine 仍可扛住部分读请求,配合主动刷新机制,避免流量全压 DB。
- 服务熔断与限流:用 Sentinel 或 Resilience4j 对 DB 查询接口做 QPS 限流(如单接口 200 QPS),超阈值直接返回兜底数据或友好提示,保护 DB 不被拖垮。
真正健壮的缓存方案,从来不是堆砌一种技术,而是分层设防:入口校验挡一部分、布隆过滤器筛掉大部分非法请求、互斥锁控住热点刷新、本地缓存兜住 Redis 故障、限流熔断守住最后底线。每层成本不高,合起来就是生产环境的底气。


















