Redis缓存雪崩是指在同一时间内,大量的key同时失效,导致大量的查询请求直接打到数据库,给数据库带来巨大压力,可以称为“瞬时数据库负载增加,引起系统响应变慢甚至宕机”。
预防措施:
- 设置随机的过期时间:给每个key的过期时间加上一个随机值,避免同时失效。
- 使用锁或队列:更新数据库操作时使用分布式锁或者使用队列来控制访问量。
- 数据预热:启动时预先加载热点数据到缓存中。
应对措施:
- 缓存数据预热:启动时预先加载热点数据到缓存中。
- 设置缓存的高可用性:使用Redis Sentinel或Redis Cluster。
- 实现缓存缓冲区:当缓存失效时,通过加锁或队列来控制写数据库的操作,避免直接压力数据库。
- 服务降级:在缓存失效后,可以暂时提供静态或默认响应,而不是直接报错。
解决方案:
// 示例代码,使用Redis锁来控制缓存更新操作
public String getWithLock(Jedis jedis, String key, String lockKey, String defaultValue) {
String value = jedis.get(key);
if (value == null) {
// 使用分布式锁,避免多线程时重复更新缓存
if (jedis.setnx(lockKey, "1") == 1) {
// 获取锁成功,设置锁的过期时间,避免死锁
jedis.pexpire(lockKey, 1000);
try {
// 重新从数据库获取数据,并更新到缓存
value = db.get(key);
jedis.set(key, value);
jedis.expire(key, 300); // 设置缓存的过期时间
} finally {
// 释放锁
jedis.del(lockKey);
}
} else {
// 其他线程在更新缓存,等待一段时间后重试
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
getWithLock(jedis, key, lockKey, defaultValue); // 递归调用
}
}
return value != null ? value : defaultValue;
}
在实际应用中,还需要根据具体的业务场景来调整和优化解决方案。