Redis 雪崩、击穿、穿透、预热、降级初了解
缓存击穿
缓存击穿指的是某个 key 一直在扛着高并发,也就是说大量的请求都是获取这个 key 对应的值。
而这个 key 在某个时间突然失效了,就意味着大量的请求无法在缓存中获取数据,转而去请求数据库。这样很有可能导致数据库被击垮,这就是缓存击穿。
解决方法:既然这个 key 这么受欢迎,那么就不要设置过期时间了,如果该 key 数据更新了,那么就通过互斥锁的方式将其更新。
使用互斥锁是为了保证缓存和数据库的一致性。
缓存雪崩
Redis雪崩我们一般称为缓存雪崩,意思就是说在某个时间节点,大量的 key 失效,导致大量的请求从缓存获取不到数据而去请求数据库。
灰色部分表示缓存无效,也就是意味着所有的请求都需要到数据库中去查询,导致数据库压力剧增,这种情况下重启也是无用的,因为重启之后依然会有大量的请求进入数据库。
缓存雪崩解决方案一:加随机值
缓存雪崩是由于某一时间点,大量的 key 失效导致的,解决问题关键在于如何防止同一时间点发生大量的 key 失效。
最简单的情况就是把 key 的过期时间分散开,在设置 key 的过期时间时再加上一个随机值,这样就可以解决缓存雪崩的问题了。
缓存雪崩解决方案二:加锁
流程是这样子的,在多个请求同时到达业务系统时候,只能有一个线程能获取到锁,然后才能继续去缓存或者是数据库中查询数据,然后后面的流程和之前的是一样的,执行完成后释放锁,然后其他线程再争抢锁,然后重复前面的流程。
优点是可以很好的保护数据库不会被打挂,缺点就是并发度极低。
优化:
缓存穿透
缓存穿透意思就是一个不存在的 key 一直被访问,结果发现数据库中也没有这样的数据,最终导致访问该 key 的所有请求都直接请求到数据库中。
缓存穿透解决方案一:缓存空数据
什么是缓存空数据?就是假设某个 key 数据并不存在,那就存一个 NULL ,但是要设置过期时间,不然如果数据库新增了这条记录,依旧查不到(数据库不一致)。
缓存穿透解决方案二:布隆过滤器
布隆过滤器是一种数据结构,更准确的说是一种概率型数据结构,可以判断某个元素一定不存在或可能存在。
布隆过滤器由一个很长的 bit 数组和一系列 hash 函数构成。
初始数组全部为0,目标元素为 a1 、 a2 ,经过一系列 hash 之后,得到的下标分别为1、3、5和5、7、9,将这些下标元素置为1。测试元素 b1 经过一系列 hash 之后得到2、3、15,因为2和15位置为0,所以可以判断出 b1 一定不存在;测试元素 b2 经过一系列 hash 之后得到3、5、7, 我们知道元素 b2 是不存在的,但是现在得到的3、5、7位置为1,可见布隆过滤器用来判断元素存在是不正确的,只能判断出元素一定不存在,但结果存在时,元素不一定存在。
使用 google 包下的类来进行测试
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.nio.charset.Charset;
/**
* @author wusl
* @description 测试类,创建一个插入对象为一亿,误报率为0.01%的布隆过滤器
* @date 2021/04/12
*/
public class Test {
@org.junit.Test
public void testBL(){
BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")),
100000000,
0.0001);
bloomFilter.put("slwu");
bloomFilter.put("springboot");
bloomFilter.put("Redis");
System.out.println(bloomFilter.mightContain("Redis"));
System.out.println(bloomFilter.mightContain("slwu"));
System.out.println(bloomFilter.mightContain("mysql"));
System.out.println(bloomFilter.mightContain("Java"));
}
}
缓存预热
所谓的缓存预热就是将一些可能经常使用的数据,在系统启动的时候预先设置到缓存中,这样可以避免在使用到的时候先去数据库中查询。
还有一种方式就是添加一个缓存刷新页,这样通过人工干预的方式将一些可能为热点的 key 添加到缓存中。
缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。本文将介绍一些笔者在实际工作中遇到的或见到过的一些降级方案供大家参考。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。