题目
如何解决Redis缓存中的雪崩、击穿和穿透问题?
信息
- 类型:问答
- 难度:⭐⭐
考点
缓存雪崩,缓存击穿,缓存穿透,Redis高可用设计
快速回答
解决Redis三大缓存问题的核心方案:
- 缓存雪崩:过期时间随机化 + Redis集群高可用
- 缓存击穿:互斥锁(Mutex Lock)或逻辑过期
- 缓存穿透:布隆过滤器(Bloom Filter) + 空值缓存
所有方案需配合数据库限流降级策略。
解析
1. 问题原理说明
- 缓存雪崩:大量缓存同时过期,导致请求直接打到数据库
- 缓存击穿:热点Key突然失效,高并发请求穿透到数据库
- 缓存穿透:查询不存在的数据(如id=-1),绕过缓存直接访问数据库
2. 解决方案与代码示例
缓存雪崩解决方案
// 设置缓存时添加随机过期时间(基础值+随机偏移)
int baseExpire = 3600; // 基础过期时间1小时
int randomExpire = new Random().nextInt(300); // 0-5分钟随机偏移
redis.set("key", "value", "EX", baseExpire + randomExpire);高可用设计:Redis Cluster分片 + Sentinel哨兵机制,确保单点故障时自动切换。
缓存击穿解决方案(互斥锁示例)
public String getData(String key) {
String data = redis.get(key);
if (data == null) {
if (redis.setnx("lock:" + key, "1")) { // 获取分布式锁
redis.expire("lock:" + key, 10); // 设置锁超时
data = db.query(key); // 查数据库
redis.set(key, data, "EX", 60);
redis.del("lock:" + key); // 释放锁
} else {
Thread.sleep(100); // 等待重试
return getData(key); // 递归重试
}
}
return data;
}缓存穿透解决方案(布隆过滤器)
# 初始化布隆过滤器(需第三方库如RedisBloom)
redis.execute_command("BF.RESERVE", "user_filter", 0.01, 1000000)
# 数据库数据预热到布隆过滤器
for id in db.query("SELECT id FROM users"):
redis.execute_command("BF.ADD", "user_filter", id)
# 查询流程
def get_user(user_id):
if not redis.execute_command("BF.EXISTS", "user_filter", user_id):
return None # 直接拦截非法请求
# ...正常缓存查询逻辑...3. 最佳实践
- 雪崩预防:过期时间分散 + 永不过期热点数据 + 服务熔断(如Hystrix)
- 击穿优化:双重检查锁 + 锁超时时间控制(避免死锁)
- 穿透防御:接口层参数校验 + 布隆过滤器内存优化(错误率0.1%时每元素约1.2字节)
4. 常见错误
- 未设置锁超时导致死锁
- 布隆过滤器未预热数据
- 过度依赖缓存未设计数据库降级
- 随机过期时间范围不合理(如100万数据仅设置±10秒偏移)
5. 扩展知识
- 布隆过滤器原理:多个哈希函数映射位数组,存在假阳性(false positive)但无假阴性
- Redis模块:RedisBloom官方模块提供生产级布隆过滤器
- 熔断框架:Sentinel或Hystrix实现请求限流,数据库QPS超过阈值时直接返回默认值
- 监控指标:缓存命中率(Redis info keyspace_hits)、穿透请求量(DB查询计数)