什么是缓存三兄弟?
缓存穿透,缓存击穿,缓存雪崩; 是面试官常问的Redis高频知识点;
缓存穿透
1.什么是缓存穿透?
用户查询数据在redis中不存在,并且在MySQL中也不存在,不会将该数据写入到Redis中,导致每次请求都会被打到数据库中,给数据库带来巨大压力,严重可能会出现数据库宕机;
2.解决方案
方案一:缓存空数据,如果查询结果为空,也将查询结果以{key:id,value:null}的形式存放到Redis中;
优点:操作简单,实现方便; 弊端:如果Redis中存放大量空数据,会占用大量内存;如果此时数据库中已经添加了id=0之前没有的数据,就会导致数据库和Redis中数据不一致的问题;
方案二:利用布隆过滤器 (bitMap 位图);它的底层是初始化了一个很大的数组,只能存放0和1;数组中的各个元素初始值全是 0,当一个key来了之后会经过三次hash函数的取模 (数组长度) 计算,将对应求得数组下标的对应值修改为1;利用数组中三个位置标明一个key的存在;查找过程一致:先将key经过三次hasn函数运算,得到三个数组下标,如果三个下标所对应的数组的值都为1,则说明这个key在Redis中存在,如果不是都为1,那么就不存在
注意:布隆过滤器可能会存在误判,一个在Redis中不存在的key,经过三次取模运算,得到数组中的元素也全是1,这是可能存在的,而且是不可避免地!!!
注意:利用布隆过滤器的前提是布隆过滤器中存在数据,必须先进行缓存预热
优点:占用内存小,不存在空值;
弊端:实现复杂,且存在误判,无法避免;
缓存击穿
什么是缓存击穿?
有一个热点key设置了过期时间,在这个key过期的时间点,刚好有大量并发请求来查询这个key,这些并发请求会瞬间将数据库压垮;
解决方案一:设置互斥锁;同一时间内,只允许一个线程访问这个key所对应的数据,对访问数据过程加上互斥锁,如果这个key过期,那么会进行缓存重建(向数据库查询,然后缓存到Redis)当缓存重构成功后,当前线程才会释放锁;如果在线程1占用互斥锁时,此时还有另外一些线程想要请求同一个key,那么它们就将进入等待过程;互斥锁释放后,才能访问到对应key的数据并且返回数据;
特点是:强一致,性能差 适用场景:银行(涉及到金额时可以使用);
解决方案二:设置逻辑过期时间,将过期时间设置成缓存数据的一个字段,一同存放到Redis中 {key:id,value:data}(data中包含 expire字段 );如果当热点 key 过期时,这时刚好有线程来请求这个key所对应的数据,首先会加上互斥锁,但跟方案一不同的是,该线程会创建一个新线程来完成缓存重建的过程,包括向数据库获取数据,重置缓存过期时间这个过程,最终释放锁;在新线程进行缓存重建的过程时,原线程不会等待结果,而是直接返回过期数据; 如果缓存重建的过程中,有另外新线程来访问这个key,新线程并不会等待,而是返回过期数据;只有当缓存重建的过程结束,此时如果有新线程来访问这个key,此时的数据才是重建的新数据;
特点:高可用,性能优,但不能保证数据一致性;适用场景:互联网企业的部分业务;
缓存雪崩
什么是缓存雪崩?
同一时间内大量key过期或者Redis宕机,导致大量请求到达数据库,给数据库带来巨大压力;
解决方案:
方案一:如果是由于key同时失效,可以对于不同key设置不同的过期时间 (实现key过期时间的随机性);
方案二:如果是由于Redis服务宕机,可以考虑搭建Redis高可用集群,例如哨兵模式;
方案三:给缓存业务添加降级限流策略 (可以作为缓存穿透,击穿,雪崩的兜底策略) ;nginx反向代理,SpringCloud的gateway 网关进行设置;
方案四: 给业务添加多级缓存,使用Guava 或者 Caffeine 作为一级缓存,Redis作为二级缓存;
这是我的个人总结,希望能够帮到大家。