缓存雪崩的本质是Redis集群宕机导致所有请求绕过缓存直击数据库,引发DB QPS暴增、连接池打满、CPU 100%而崩溃;Java中需通过熔断器切断“缓存未命中→查库”链路,改走降级路径,并配合本地缓存+异步回源+分级熔断+预热等策略防控。

Redis集群整体宕机时,缓存雪崩的本质是什么
不是“缓存没数据”,而是“所有请求瞬间绕过缓存直击数据库”——Redis集群一挂,redis.get() 全部抛异常或超时,业务代码若没做兜底,就会无差别调用 userMapper.selectById() 这类数据库操作。数据库QPS从几百飙到几万,连接池打满、CPU 100%、慢查询堆积,5分钟内可能就不可逆崩溃。
Java项目里,怎么让Redis宕机时不连数据库
核心是切断“缓存未命中 → 查库”的默认链路,改走降级路径。重点不在Redis客户端配置,而在业务方法的控制流改造:
- 别依赖
try-catch捕获JedisConnectionException后再查库——太晚,异常已发生,且并发下大量线程卡在DB连接上 - 用熔断器(如
Sentinel)监控redis.get()的失败率和响应时间,失败率 > 50% 或平均延迟 > 200ms 时自动打开熔断器 - 熔断开启后,直接返回预设兜底值:
new User().setUsername("游客").setLevel(0),或从本地缓存(如Caffeine)读取静态快照 - 避免全局熔断:按 key 前缀分级,比如
"user:*"熔断不影响"config:*",防止配置类数据也失效
为什么光靠哨兵/集群高可用还不够
哨兵能切主,但切换需要时间(通常 2–10 秒),这期间所有 set/get 请求都会失败;集群分片故障也可能只影响某几个 slot,导致部分 key 永远无法访问。更关键的是:即使 Redis 恢复了,缓存是空的——冷启动阶段照样雪崩。
- 哨兵不解决“数据丢失后如何不压垮DB”的问题,它只管服务地址是否可连
- 集群节点数越多,故障传播越快:一个节点网络抖动可能触发多个分片重平衡,放大延迟
- 必须配合“缓存预热”:在运维重启集群前,用脚本调用
redisTemplate.opsForValue().set()提前灌入热点 key,而不是等用户请求来触发
本地缓存 + 异步回源,怎么防穿透又防雪崩
本地缓存(如 Caffeine)不是用来替代 Redis,而是当 Redis 不可用时的“缓冲垫”。难点在于更新一致性与空值处理:
- 本地缓存过期时间要明显短于 Redis(如 Redis 设 1h,本地设 5min),避免 Redis 恢复后本地还挂着脏数据
- 数据库查不到时,仍要写本地缓存(
cache.put(key, null)),但过期时间设成 60 秒,防止缓存穿透放大 - 回源必须异步:用线程池提交
updateRedisAndLocalCache(key),主流程绝不等待,否则本地缓存失效瞬间也会引发并发查库 - 别用
synchronized锁本地变量——锁粒度太粗,会把所有请求串行化;改用ConcurrentHashMap.computeIfAbsent()做轻量级互斥
最常被忽略的一点:降级逻辑必须和 Redis 客户端解耦。不要在 RedisTemplate 的 afterPropertiesSet() 里埋降级开关,而应统一收口到一个 CacheService.get(String key, Supplier<t> fallback)</t> 方法里——这样未来换成其他缓存中间件,降级策略不用动一行。

















