Redis缓存雪崩
我们直接用一个 缓存雪崩 的例子来演示整个流程,让你看到“高并发请求 + 缓存同时过期 → DB 压力爆炸”的刺激场景。下面用 Spring + Redis + MySQL 来说明,方便你直接理解和模拟。
场景
假设有一个热门商品列表,缓存 key 为
hot:products
。缓存过期时间统一设置成 10秒。
模拟 大量并发请求 在缓存过期后同时访问 DB → 缓存雪崩发生。
1️⃣ 缓存雪崩示例代码
@Service
public class ProductService {@Autowiredprivate ProductMapper productMapper; // MyBatis 操作 DB@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final String CACHE_KEY = "hot:products";// 简单读取方法(不做任何保护,模拟雪崩)public List<Product> getHotProducts() {List<Product> products = (List<Product>) redisTemplate.opsForValue().get(CACHE_KEY);if (products != null) {return products;}// 缓存没命中 → 查询 DBproducts = productMapper.findHotProducts();// 写回缓存,设置统一过期时间redisTemplate.opsForValue().set(CACHE_KEY, products, 10, TimeUnit.SECONDS);return products;}
}
2️⃣ 模拟高并发访问
假设同时有100w人访问这个热门产品。
3️⃣ 现象分析
缓存刚过期,100w个请求同时到达
getHotProducts()
。缓存 miss → 100w个请求全部访问 DB。
DB 承受巨大压力,如果更高并发可能直接宕机或延迟暴增。
最终只有一条或少数几条请求回填缓存,其他的都浪费了 DB 资源。
4️⃣ 改进策略(防止雪崩)
加随机过期时间
// 避免同时过期
long expire = 10 + new Random().nextInt(5); // 10~15秒
redisTemplate.opsForValue().set(CACHE_KEY, products, expire, TimeUnit.SECONDS);
加互斥锁 / 单线程去 DB 回填
RLock lock = redissonClient.getLock(CACHE_KEY + ":lock");
lock.lock();
try {products = productMapper.findHotProducts();redisTemplate.opsForValue().set(CACHE_KEY, products, 10, TimeUnit.SECONDS);
} finally {lock.unlock();
}
保证同一时间只有一个线程去 DB 回填,其它线程等待或读取缓存。
使用异步刷新 / 预热缓存
缓存临近过期时提前刷新,不让大量请求打到 DB。
可用定时任务 + 异步刷新。
💡 小结
缓存雪崩就是缓存集中过期导致 DB 瞬间压力暴增。
防护策略主要有:
随机过期(分散时间)
加互斥锁(单线程回填)
异步刷新 / 预热(提前回填)
实际项目一般多策略结合使用,保证高并发下 DB 安全。