版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/LuQiaoYa/article/details/89243231
什么是缓存雪崩,缓存穿透,缓存预热,缓存更新,缓存降级?
一、缓存雪崩:
二、缓存穿透
三、缓存预热
四、缓存更新
五、缓存降级
一、缓存雪崩:
由于原有缓存失效,新缓存未到期间(例如我们设置缓存采用了一样的过期时间,在同一时刻造成了所有的缓存失效),所有原本应该去访问缓存的请求都去查询数据库,
对数据库CPU和内存造成了巨大的压力,严重的会造成数据库宕机。
缓存雪崩的解决方案:
(1)如果并发量不是特别多的话,使用最多的方案是加锁排队。
public void getAllList(){
int cacheTime = 30;
String cacheKey = "list_key";
String lockKey = cacheKey;
String value = CacheHelper.get(cacheKey);
if(value != null){
// 如果缓存有值,就直接取出来即可
return value;
}else{
synchronized(lockKey){
value = CacheHelper.get(cacheKey);
if(value != null){
return value;
}else{
// 这里一般是SQL查询语句
value = GetListFromDB();
// 把查询结果放入缓存
CacheHelper.Add(cacheKey,value,cacheTime)
}
}
return value ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(2)给每一个缓存增加相应的缓存标记,记录缓存是否有效,如果缓存标记失效,则更新数据缓存,伪代码如下:
public void getAllListNew(){
int cacheTime = 30;
String cacheKey = "list_key";
// 缓存标记
String signKey = cacheKey + "_sign";
String sign = CacheHelper.get(signKey);
// 获取缓存值
String cacheValue = CacheHelper.get(cacheKey);
if(sign != null){
//没有过期
return cacheValue;
}else{
CacheHelper.Add(signKey,"1",cacheTime);
ThreadPool.QueueUserWorkItem((args) ->{
// 这里一般是SQL查询语句
cacheValue = GetListFromDB();
CacheHelper.Add(cacheKey,cacheValue,cacheTime*2);
})
return cacheValue;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
缓存标记:记录缓存数据是否过期,如果过期,则会通知另外的线程在后台去更新实际key的值。
缓存数据:缓存数据的过期时间比缓存标记的时间延长一倍,如果,缓存标记过期时间是30分钟,缓存数据的过期时间是60分钟,这样当缓存标记过期后,还能把缓存中放的旧数据返回给调用端,直到另外的线程再后台更新完成后才会返回新缓存。
二、缓存穿透
缓存穿透是指用户在数据库查询查不到,在缓存中自然也不会有,这样就导致每次用户查询的时候在缓存查不到,在数据库也查不到,相当于进行了两次无用查询。
缓存穿透解决方案:
(1)采用布隆过滤器。将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
布隆过滤器:
布隆过滤器相当于一个不怎么精确的set结构,布隆过滤器有两个基本指令,bf.add添加元素,bg.exits查询元素是否存在。要想一次添加多个元素,用bf.madd指令。
要一次查找是否存在多个元素,用bf.mexits指令。
布隆过滤器的原理如图:
每个布隆过滤器对应到Redis的数据结构里面就是一个大型的位数组和几个不一样的无偏hash函数,所谓无偏就是能把hash值算的比较均匀。
向布隆过滤器中添加key值时,会使用多个hash函数对key值进行hash算法取得一个哈希值,然后对数组长度进行取模运算得到一个下标位置,每个hash函数都会得到一个不同的位置,最后得到多个位置,再把位数组的这几个位置都置为1就完成了add操作。
向布隆过滤器中查询某个key值时,也跟add操作一样,算出来hash的几个位置,看看这几个位置是否都为1,有一个为0就说明布隆过滤器中该值不存在。如果这几个位置都是1,
也不一定说明这个key存在,也有可能是别的key值存在导致。
Redis中使用布隆过滤器:
布隆过滤器可能存在误判的情况,Redis中有两个值可以决定布隆过滤器的准确率。
error_rate:允许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间就越大。
initial_size:布隆过滤器可以存储的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降。
Redis中有一个命令可以来设置这两个值:
bf.reserve urls 0.01 100
1
三个参数的含义:
第一个是过滤器的名字
第二个是error_rate的值
第三个是initial_size的值
使用这个命令要注意,执行这个命令之前这个过滤器应该是不存在的,如果存在的话会报错。
(2)如果一个查询的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。这样下一次访问缓存就有值了,而不会继续访问数据库。
三、缓存预热
缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统中,避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。
解决方案:
(1)直接写个缓存页面,上线时手工操作下
(2)数据量不大,可以在项目启动的时候自动加载
(3)定时刷新缓存
四、缓存更新
除了缓存自带的缓存失效策略之外,我们还可以进行自定义的缓存淘汰策略,常见的策略有两种:
(1)定期去清理过期的缓存
(2)当有用户请求过来时,再判断这个请求用到的key是否过期,过期的话就去获取新数据并更新缓存。
五、缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。
系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
————————————————
版权声明:本文为CSDN博主「LuQiaoYa」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/LuQiaoYa/article/details/89243231