
突发故障数据库 CPU 飙升到 100%上周五下午运维群里突然报警核心订单服务的 MySQL 数据库 CPU 占用率瞬间飙升到 100%大量接口响应超时。经过紧急排查日志和监控我们发现接口报错集中在“获取商品详情”模块。数据库慢查询激增全是针对 product_info 表的复杂查询。此时 Redis 集群的命中率从平时的 99% 骤降到了 30% 以下。初步判断典型的缓存雪崩现象。原因分析为什么会出现大面积缓存失效我们检查了 Redis 的 Key 过期时间设置发现了一个历史遗留的“坑”当初为了省事开发同学将所有商品详情的缓存过期时间统一设置为了 3600秒1小时。当这上万个 Key 在同一秒集中过期时恰好赶上了一波流量高峰。海量请求直接穿透缓存打到了 MySQL 上导致数据库瞬间被击垮。解决方案与代码落地为了彻底解决这个问题我们进行了两步改造3.1 过期时间加随机值防雪崩在设置缓存时给基础过期时间加上一个随机偏移量打散 Key 的过期时间点。// 基础过期时间 1小时 0~10分钟的随机时间intbaseExpire3600;intrandomExpirenewRandom().nextInt(600);redisTemplate.opsForValue().set(key,value,baseExpirerandomExpire,TimeUnit.SECONDS);3.2 引入互斥锁防穿透对于热点数据当缓存未命中时只允许一个线程去查数据库其他线程等待或返回旧数据防止并发查询压垮 DB。publicProductgetProduct(Stringid){StringcacheKeyproduct:id;Productproduct(Product)redisTemplate.opsForValue().get(cacheKey);if(product!null)returnproduct;// 尝试获取分布式锁StringlockKeylock:product:id;booleanlockedredisTemplate.opsForValue().setIfAbsent(lockKey,1,10,TimeUnit.SECONDS);if(locked){try{// 双重检查product(Product)redisTemplate.opsForValue().get(cacheKey);if(product!null)returnproduct;// 查数据库并回写缓存productproductMapper.selectById(id);redisTemplate.opsForValue().set(cacheKey,product,3600,TimeUnit.SECONDS);}finally{redisTemplate.delete(lockKey);// 释放锁}}else{Thread.sleep(50);// 稍作等待后重试returngetProduct(id);}returnproduct;}经验总结这次故障给我们敲响了警钟。在使用 Redis 时永远不要假设缓存是绝对可靠的。核心建议生产环境严禁使用统一的固定过期时间。核心链路必须做好兜底策略如本地缓存 Caffeine Redis 多级缓存。上线前一定要做全链路压测模拟缓存失效的极端场景。