在分布式缓存预热场景中,惊群效应是指当缓存失效或系统启动时,大量并发请求同时涌入后端(数据库或下游服务),导致瞬间负载飙升、系统响应变慢甚至崩溃的现象。
一、惊群效应的本质
1.1 什么是惊群效应
典型场景:
系统刚启动,本地缓存为空,1000 个并发请求同时到达
每个请求都发现缓存缺失,同时去查询 Redis 或数据库
瞬间产生 1000 个后端请求,造成资源浪费和系统压力
1.2 预热期间的特殊性
| 场景 | 惊群原因 | 影响程度 |
|---|---|---|
| 系统冷启动 | 所有实例本地缓存全空 | ⚠️ 严重 |
| 本地缓存过期 | Caffeine/Guava 批量失效 | ⚠️ 中等 |
| Redis 重启后 | Redis 缓存全空 | 🔥 灾难性 |
| 热点数据重建 | 单个热点 Key 失效 | ⚠️ 局部但可能严重 |
二、解决方案
2.1 方案一:互斥锁(Mutex)—— 最经典
核心思想:只允许一个请求去加载数据,其他请求等待或直接返回旧值。
java
@Service public class CacheWithMutex { private final Cache<String, Object> localCache = Caffeine.newBuilder() .maximumSize(10_000) .expireAfterWrite(5, TimeUnit.MINUTES) .build(); private final Map<String, Lock> lockMap = new ConcurrentHashMap<>(); public Object get(String key) { // 1. 快速路径:缓存命中直接返回 Object value = localCache.getIfPresent(key); if (value != null) { return value; } // 2. 获取或创建该 Key 的锁 Lock lock = lockMap.computeIfAbsent(key, k -> new ReentrantLock()); lock.lock(); try { // 3. 双重检查:获取锁后可能已被其他线程加载 value = localCache.getIfPresent(key); if (value != null) { return value; } // 4. 加载数据(只有一个线程执行) value = loadFromBackend(key); localCache.put(key, value); return value; } finally { lock.unlock(); // 清理锁(可选,避免内存泄漏) lockMap.remove(key, lock); } } }优化:使用分布式锁(多实例场景)
java
@Service public class DistributedCacheWithMutex { @Autowired private RedissonClient redissonClient; public Object get(String key) { // 1. 查本地缓存 Object value = localCache.getIfPresent(key); if (value != null) return value; // 2