Redis 缓存穿透,雪崩,击穿现象与解决办法
缓存穿透
什么是缓存穿透
缓存穿透,是指查询一个数据库一定不存在的数据。
例如:对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。这种恶意攻击场景的缓存穿透就会直接把数据库给打死
解决办法
- 采用布隆过滤器BloomFilter
将所有可能存在的数据哈 希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
- 缓存空值
如果一个查询返回的数据为空(不管是数据不 存在,还是系统故障)我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库
- 接口限流与熔断、降级
重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。
缓存雪崩
什么是缓存雪崩
缓存雪崩,是指在某一个时间段,缓存集中过期失效。
举个栗子:如果首页所有 Key 的失效时间都是 12 小时,中午 12 点刷新的,我零点有个大促活动大量用户涌入,假设每秒 6000 个请求,本来缓存可以抗住每秒 5000 个请求,但是缓存中所有 Key 都失效了。此时 6000 个/秒的请求全部落在了数据库上,数据库必然扛不住,真实情况可能 DBA 都没反应过来直接挂了。
解决办法
- 加锁排队
key: whiltList value:1000w个uid 指定setNx whiltList value nullValue mutex互斥锁解决,Redis的SETNX去set一个mutex key, 当操作返回成功时,再进行load db的操作并回设缓存; 否则,就重试整个get缓存的方法
- 数据预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key
- 双层缓存策略
C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期。
- 定时更新缓存策略
失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存,
- 不同过期时间
设置不同的过期时间,让缓存失效的时间点尽量均匀
- 热点数据永不过期
部分用户访问特别频繁的热点数据,设置永不过期
缓存击穿
什么是缓存击穿
缓存击穿是指一个 Key 非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个 Key 在失效的瞬间,持续的大并发直接落到了数据库上,就在这个 Key 的点上击穿了缓存。
解决办法
- 互斥锁
public static String getData(String key) throws InterruptedException {
// 从Redis查询数据
String result = getDataByKV(key);
// 参数校验
if (StringUtils.isBlank(result)) {
try {
// 获得锁
if (reenLock.tryLock()) {
// 去数据库查询
result = getDataByDB(key);
// 校验
if (StringUtils.isNotBlank(result)) {
// 插进缓存
setDataToKV(key, result);
}
} else {
// 睡一会再拿
Thread.sleep(100L);
result = getData(key);
}
} finally {
// 释放锁
reenLock.unlock();
}
}
return result;
}
- 热点数据永不过期
不过期将不存在该问题