会,缓存雪崩会导致读写均急剧变慢,因数据库连接池耗尽、写操作依赖读逻辑卡住、cpu/io过载及orm缓存降级形成负向循环;加随机ttl需注意偏移量范围、固定key偏移、服务端时间;双层缓存需支持脏读与监控;redis故障时写操作应走异步补偿或降级直写db。

会,而且下降得非常剧烈——不是“略有影响”,而是数据库在几秒内被压垮,读写延迟从几毫秒飙到数秒甚至超时,写操作也可能因连接池耗尽而阻塞或失败。
缓存雪崩为什么让读写都变慢
缓存雪崩不是只影响“读”,它会通过连锁反应拖垮整个数据链路:
- 读请求失效后涌向数据库,瞬间占满数据库连接池,新来的读、写请求都拿不到连接,
wait_timeout或max_connections耗尽是常见现象 - 写操作常依赖读(比如先查再更新),查不到缓存 → 查库 → 查库慢 → 写逻辑卡住 → 写入延迟上升甚至超时
- 数据库 CPU/IO 拉满后,自身 WAL 写入、事务提交、索引维护等后台动作也变慢,进一步恶化所有 SQL 的执行时间
- 如果用了 ORM(如 MyBatis、Hibernate),一级/二级缓存可能因数据库响应慢而失效或降级,形成负向循环
加随机 TTL 是最简单但最容易翻车的操作
很多人直接套用 ttl + new Random().nextInt(300),但没注意几个关键点:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 随机范围不能太小:比如基础 TTL 是 30 分钟,只加 ±30 秒,对高并发场景毫无意义;建议偏移量至少占基础 TTL 的 10%~30%,例如
1800 + rand.Intn(540)(30±9 分钟) - 不能在每次
set时都重新生成随机值:如果一个 key 多次刷新缓存,每次 TTL 都不同,会导致过期时间混乱,反而更难预测失效节奏;应固定该 key 的随机偏移(比如用 key 的 hash 值取模) - 注意客户端时钟漂移:如果业务部署在多个时区或 NTP 不稳的机器上,
System.currentTimeMillis()可能偏差几百毫秒,影响实际过期分布,优先用 Redis 服务端时间(TIME命令或 Lua 脚本中redis.call('PEXPIREAT', ...))
本地缓存 + Redis 双层结构真能扛住雪崩吗
能,但前提是设计得当——很多项目加了 Caffeine 就以为万事大吉,结果雪崩时本地缓存照样被击穿:
- 本地缓存必须允许“脏读”:即缓存过期后不立即淘汰,而是设为
refreshAfterWrite模式,后台异步加载新值,前台继续返回旧值(需业务接受短暂不一致) - 不能只靠
maximumSize和expireAfterWrite:这俩只是被动淘汰策略,对突发流量无缓冲作用;必须配合recordStats()+ 监控 miss rate,一旦cache.get(key)的missCount短时飙升,就要触发告警或自动降级 - 本地缓存的 key 命名要和 Redis 对齐,避免同一条数据在两层缓存里存成不同 key,导致预热失效或双写不一致
- 注意 JVM 堆压力:Caffeine 默认用软引用+弱引用管理 entry,但在高并发下可能引发频繁 GC,建议显式配置
weigher控制单条数据内存占用
Redis 故障时,写操作到底该阻塞还是丢弃
没有银弹,取决于业务语义。但可以明确的是:盲目重试或无限等待一定会让问题更糟。
- 写前校验缓存可用性不现实:每次
SET前都PINGRedis,会引入额外 RTT,且 PING 通 ≠ 服务可写(比如主从同步中断、OOM 时拒绝写入) - 推荐做法是结合
try-with-resources+RedisCallback封装写操作,在 catch 到RedisConnectionFailureException或JedisConnectionException后,走 fallback 流程:记录日志 + 发送 MQ 消息 + 返回 success(异步补偿) - 对强一致性写(如扣库存),必须启用降级开关:比如配置中心下发
redis.write.enabled=false,此时写操作转为直写 DB,并标记“待补缓存”,等 Redis 恢复后由定时任务批量回填 - 绝对不要在写失败时 sleep 后重试:这会把瞬时故障变成线程池阻塞,
ThreadPoolExecutor的queue填满后,新请求直接被 reject,比数据库慢更致命
真正容易被忽略的点是:缓存雪崩从来不是单一组件的问题,它是缓存策略、数据库容量、应用线程模型、监控告警响应速度共同决定的临界点。一个 EXPIRE 参数改错,可能要靠熔断阈值、连接池大小、GC 参数三者联动才能兜住——别只盯着 Redis 看。










