【redis】redis缓存穿透及解决方案|缓存穿透,缓存击穿,雪崩的理解

|目录

缓存穿透

解决方案

布隆过滤

缓存空对象

缓存雪崩

解决方案

1、保证缓存层服务高可用性

2、依赖隔离组件为后端限流并降级

3、数据预热

4.做二级缓存,或者双缓存策略。

 5.缓存永远不过期

缓存并发

算法说明

布隆过滤器


原文链接:https://blog.csdn.net/it_townlet/article/details/88217607

缓存穿透

缓存穿透是指查询一个一定不存在的数据,由于缓存不命中,接着查询数据库也无法查询出结果,因此也不会写入到缓存中,这将会导致每个查询都会去请求数据库,造成缓存穿透;

用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

缓存访问的过程如下:

(1)应用访问缓存,假如数据存在,则直接返回数据

在这里插入图片描述

(2)数据在redis不存在,则去访问数据库,数据库查询到了直接返回应用,同时把结果写回redis

在这里插入图片描述

(3)数据在redis不存在,数据库也不存在,返回空,一般来说空值是不会写入redis的,如果反复请求同一条数据,那么则会发生缓存穿透。

在这里插入图片描述

当然解决方案是可以为这个key设置一个空值,同时写入redis,下次请求的时候就不会访问数据库,但是如果每次请求的是不同的key,同时这个key在数据库中也是不存在的,那这样依然会发生缓存穿透。

解决方案

布隆过滤

对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;

缓存空对象

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;

但是这种方法会存在两个问题:

  1. 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;

  2. 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

比较

缓存雪崩

缓存雪崩是指,由于缓存层承载着大量请求,有效的保护了存储层,但是如果缓存层由于某些原因整体不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

解决方案

1、保证缓存层服务高可用性

即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务,比如 Redis Sentinel 和 Redis Cluster 都实现了高可用。

2、依赖隔离组件为后端限流并降级

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

 1. 加锁排队. 限流-- 限流算法. 1.计数 2.滑动窗口 3.  令牌桶Token Bucket 4.漏桶 leaky bucket [1]

 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

 业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。

3、数据预热

可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

4.做二级缓存,或者双缓存策略。

     A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

 5.缓存永远不过期

 这里的“永远不过期”包含两层意思:

    (1) 从缓存上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

     (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期.

 从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

缓存并发

缓存并发是指,高并发场景下同时大量查询过期的key值、最后查询数据库将缓存结果回写到缓存、造成数据库压力过大

分布式锁

在缓存更新或者过期的情况下,先获取锁,再进行更新或者从数据库中获取数据后,再释放锁,需要一定的时间等待,就可以从缓存中继续获取数据。

算法说明

布隆过滤器


我们可以这样考虑,可以先判断key值是否存在,如果不存在,则不访问redis,那这样就可以拦截大量的请求,布隆过滤器恰好可以实现这样的需求。


布隆过滤器本质是一个二进制向量,初始化的时候每一个位置都是0,如下图,比如说a经过hash算法后得到一个下标位置,接下来就会把下标的值改为1,图中所示的是每一个元素经过三次hash运算,每一个红线代表一次hash算法,为什么要运算三次呢,这是为了减少hash冲突,当然hash算法不一定是三次,经过多次不同维度的哈市算法后,就把a值映射到了二进制向量里面,这样的好处很多,可以节省空间,假如说a值是一串很长的字符串,那么经过映射后就可以只占三位长度,并且查找速度很快。

如果布隆过滤器判断元素存在,则不一定存在,如果不存在,则一定不存在
如何理解这句话,因为有可能你一个元素运算得到的下标恰好是别的元素的下标,如果经过运算后布隆过滤器判断不存在,也就是说至少有一个下标是为0的,那肯定是不存在的

布隆过滤器的使用
用Google的guava包已经有了布隆过滤器算法的实现,注意的是布隆过滤器有一定的误判率,不可能达到100%的精准,首先初始化项目的时候从数据库查询出来所有的key值,然后放到布隆过滤器中,guava包都实现了相应的put方法和hash算法。

加了布隆过滤器的过程如下

1,当应用访问的时候,先去布隆过滤器中判断kedy值,如果发觉没有key值不存在,直接返回
2、如果key值在布隆过滤器存在,则去访问redis,由于是有误判率的,所以redis也有可能不存在
3、那么这时候就去访问数据库,数据库不存在,那就直接返回空就行

如果误判率为3%,当有100万个请求同时过来的时候,布隆过滤器已经挡住了97万个请求,剩下3万个请求假如是误判的,这时候再访问数据库可以通过加锁的方式实现,只有竞争到锁了就去访问数据库,这样就完全可以解决缓存穿透问题

布隆过滤器的应用
比如说输入用户名的时候,可以马上检测出该用户名是否存在,黑名单机制,单词错误检测等
 

缓存穿透,缓存击穿,雪崩的理解

原文链接:https://blog.csdn.net/weixin_43626529/article/details/122172566

一、缓存穿透

理解:何为缓存穿透,先要了解穿透,这样有助于区分穿透和击穿,穿透就类似于伤害一点一点的累计,最终达到穿透的目的,类似于射手,一下一下普通攻击,最终杀死对方,先上图

缓存穿透

先来描述一下缓存穿透的过程:
1.由于我们取数据的原则是先查询redis上,如果redis上有数据那么我们就取redis,如果没有则对数据库进行查询
2.当我们的应用服务器压力变大(例如我们双十一进行购物)
3.那么我们同时大量数据访问redis,而redis中只有其中几条数据的信息,那么我们就会对数据库进行大量的查询(redis命中率降低)
4.导致数据库崩溃
以上的过程就是缓存穿透

解决办法:
1.对空值进行缓存
2.设置黑(白)名单
3.采用布隆过滤器(布隆底层实际原理类似于优化版的使用bitmap进行缓存查询)
 

二、缓存击穿

理解:何为缓存击穿,击穿其实就是类似于爆发伤害,类似于高爆发的法师,一套技能瞬间杀死对方,上图

 缓存击穿

先来描述一下缓存击穿的过程:
1.由于我们取数据的原则是先查询redis上,如果redis上有数据那么我们就取redis,如果没有则对数据库进行查询,击穿的结果是redis还在正常运行,mysql崩了
2.当我们数据库(mysql)访问量瞬间增加(例如我们双十一进行抢制定的商品)
3.而此时,我们的redis上相对应的key突然过期,那么我们把查询压力全给mysql
4.导致数据库崩溃
以上的过程就是缓存击穿

解决办法:
1.预先设置热门数据(例如先缓存好制定商品的信息)
2.实施调整(人工延长key过期时间)
3.使用锁(影响效率)
在这里插入图片描述

三.雪崩现象

理解:何为雪崩,顾名思义,就是一片雪花导致整个系统的崩盘,先上图

雪崩

先来描述一下缓存击穿的过程:
1.由于我们取数据的原则是先查询redis上,如果redis上有数据那么我们就取redis,如果没有则对数据库进行查询
2.当我们服务器压力变大(例如双十一购物)
3.而此时,我们的redis上大量的key过期,导致数据库压力剧增,返回给服务器的效率变低
4.由于效率变低,导致我们redis中缓存过多请求,导致redis崩溃
5.最终导致整个系统的崩盘
以上的过程就是雪崩

解决办法:
1.构建多级缓存(nginx缓存+redis缓存+其他缓存)
2.使用锁或者队列
3.设置过期标志自动更新
4.错开key的过期时间(建议3 4结合使用)

总结
1.redis妙用很多,需要在实际应用场景体会
2.思想很重要,基础也很重要
3.谢谢尚硅谷的老师
4.欢迎讨论指出错误
视频教程链接 :https://www.bilibili.com/video/BV1Rv41177Af?p=47

 

 

 

posted on 2022-10-04 01:26  bdy  阅读(2322)  评论(0编辑  收藏  举报

导航