Redis 【第三篇】Redis缓存穿透、击穿、雪崩与相对应的解决方案,以及跳跃表实现原理

一、前述

关于Redis缓存的击穿、穿透和雪崩应该是再熟悉不过的词了,也是面试常问的高频试题。
不过,对于这三大缓存的问题,有很多人背过了解决方案,却少有人能把思路给理清的,首先大致画了一个系统的简略架构图。

在这里插入图片描述

二、缓存穿透
1、简单介绍

  缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义~

  流程图
在这里插入图片描述
  正常情况下问题并不大,但是如果用户恶意重复请求在 Redis 和 DB 中都不存在的资源。那么每次请求都会直接打到 DB(Mysql) 上,严重时可能会导致物理 DB(Mysql) 宕机。

2、 解决方案

  2.1- 用户请求合法性校验
对用户的请求合法性进行校验,拦截恶意重复请求。
  2.2- 缓存空结果
如果系统发现 Redis 及 DB 中都不存在该资源,就缓存空结果一段时间。
注意:空结果的失效时间不能设置的太长,否则数据的实效性会产生很大的问题。
  2.3- 布隆过滤器
  (1)、布隆过滤器,英文叫BloomFilter,可以说是一个二进制向量和一系列随机映射函数实现。 可以用于检索一个元素是否在一个集合中。 在访问缓存层和存储层之前,将存在的key用布隆过滤器提前保存起来,做第一层拦截,当收到一个对key请求时先用布隆过滤器验证是key否存在,如果存在在进入缓存层、存储层。可以使用bitmap做布隆过滤器。这种方法适用于数据命中不高、数据相对固定、实时性低的应用场景,代码维护较为复杂,但是缓存空间占用少。
  (2)、算法描述:

  • 初始状态时,BloomFilter是一个长度为m的位数组,每一位都置为0。
  • 添加元素x时,x使用k个hash函数得到k个hash值,对m取余,对应的bit位设置为1。
  • 判断y是否属于这个集合,对y使用k个哈希函数得到k个哈希值,对m取余,所有对应的位置都是1,则认为y属于该集合(哈希冲突,可能存在误判),否则就认为y不属于该集合。可以通过增加哈希函数和增加二进制位数组的长度来降低错报率。
    注意:布隆过滤器的原理还是比较简单的。这里我们需要注意,布隆过滤器可能存在一定误判的可能性,但它依然可以帮助你拦截掉大部分一定不存在的数据。
      (3)、案例分析
    假设我们现在有一个长度为 9 的 bit 数组,该数组的每个位置上只能保存 1 或者 0,1 标识该位置被占用,0 标识该位置未被使用。
    在这里插入图片描述
Key值取模后的值
Key10、3、5
Key21、4、6
Key35、7、8

最后,我们会发现这个 bit 数组里只有位置 2还是空着的。如果此时来了一个新的 key4 通过三个Hash算法求出的哈希值为 1、2、4,我们则可以断定 key4 一定不存在。
在这里插入图片描述

二、缓存击穿
1、简单介绍

  是针对缓存中没有但数据库有的数据。场景是,当Key失效后,假如瞬间突然涌入大量的请求,来请求同一个Key,这些请求不会命中Redis,都会请求到DB,导致数据库压力过大,甚至扛不住,挂掉。
  案例:当前key是一个热点key(例如一个秒杀活动),并发量非常大,当秒杀活动开始的时候,Redis 集群中数据在此刻正好过期了,那么无数的请求则直接打到了秒杀系统的物理 DB 上,DB 瞬间挂了~

2、 解决方案

  2.1- 热点数据永远不过期

  • 从缓存层面来看,确实没有设置过期时间,所以不会出现热点key过期后产生的问题,也就是“物理”不过期。
  • 从功能层面来看,为每个value设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去更新缓存。

  2.2- 分布式互斥锁

  发现没有命中Redis,去查数据库的时候,在执行更新缓存的操作上加锁,只允许一个线程重建缓存,其它线程等待重建缓存的线程执行完,这个执行更新缓存线程访问过后,缓存中的数据会被重建,这样其他线程就可以从缓存中取值。

3、 两种方案对比
  • 分布式互斥锁:这种方案思路比较简单,但是存在一定的隐患,如果在查询数据库 + 和 重建缓存(key失效后进行了大量的计算)时间过长,也可能会存在死锁和线程池阻塞的风险,高并发情景下吞吐量会大大降低!但是这种方法能够较好地降低后端存储负载,并在一致性上做得比较好。
  • “永远不过期”:这种方案由于没有设置真正的过期时间,实际上已经不存在热点key产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。
三、缓存雪崩
1、简单介绍

  是指Redis集群中大量热点Key同时失效,对这些Key的请求又会打到DB上,同样会导致数据库压力过大甚至挂掉。

2、 解决方案

  2.1- 让Key的失效时间分散开,可以在统一的失效时间上再加一个随机值,或者使用更高级的算法分散失效时间。
  2.2- 构建多个redis实例,个别节点挂了还有别的可以用。
  2.3- 多级缓存:比如增加本地缓存,减小redis压力。
  2.4- 对存储层增加限流措施,当请求超出限制,提供降级服务(一般就是返回错误即可)。

四、Redis跳跃表

这一段时间看Redis的时候看到了跳跃表方面的问题,就顺便总结一下

1、什么是跳跃表?

  跳表是一个随机化的数据结构,实质是一种可以进行二分查找的有序链表。跳表在原有的有序链表上增加了多级索引,通过索引来实现快速查询。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。跳跃表的效率可以和平衡树想媲美了,最关键是它的实现相对于平衡树来说,代码的实现上简单很多~
  跳跃表在 Redis 中使用不是特别广泛,只用在了两个地方。一是实现有序集合键,二是集群节点中用作内部数据结构~

2、跳跃表原理图解

  2.1-来自《Redis 设计与实现》这本书中的一张完整的跳跃表的图。
在这里插入图片描述
  2.2- 跳跃表的 level 是如何定义的?
  跳跃表 level 层级完全是随机的。一般来说,层级越多,访问节点的速度越快。
  2.3- 跳跃表的插入
1)插入数据之前,链表是空的,如下图:
在这里插入图片描述
2)插入 level = 3,key = 1
在这里插入图片描述
3)插入 level = 1,key = 2
在这里插入图片描述
4)插入 level = 2,key = 3
在这里插入图片描述
5)插入 level = 3,key = 5
在这里插入图片描述
6)插入 level = 1,key = 66
在这里插入图片描述
7)插入 level = 2,key = 100
在这里插入图片描述
  2.4- 跳跃表的查询
  跳跃表的查询是从顶层往下找,那么会先从第顶层开始找,方式就是循环比较,如过顶层节点的下一个节点为空说明到达末尾,会跳到第二层,继续遍历,直到找到对应节点。
  现在我们要找键为 66 的节点的值。那跳跃表是如何进行查询的呢?
1)如下图所示红色框内,我们带着键 66 和 1 比较,发现 66 大于 1。继续找顶层的下一个节点,发现 66 也是大于五的,继续遍历。由于下一节点为空,则会跳到 level 2。
在这里插入图片描述
2)上层没有找到 66,这时跳到 level 2 进行遍历,但是这里有一个点需要注意,遍历链表不是又重新遍历。而是从 5 这个节点继续往下找下一个节点。如下,我们遍历了 level 3 后,记录下当前处在 5 这个节点,那接下来遍历是 5 往后走,发现 100 大于目标 66,所以还是继续下沉。
在这里插入图片描述
3)当到 level 1 时,发现 5 的下一个节点恰恰好是 66 ,就将结果直接返回。
在这里插入图片描述
  2.5- 跳跃表的删除
在这里插入图片描述
  2.6- 跳跃表的修改
  跳跃表的修改和数据结构中的链表修改类似,更改所需要修改的值即可。

五、淘汰策略

在这里插入图片描述

  1. 默认情况下,Redis 在使用的内存空间超过 maxmemory 值时,并不会淘汰数据,也就是设定的 noeviction 策略,写满后再写会返回错误。
  2. volatile-ttl 策略,在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。
  3. volatile-random策略,在设置了过期时间的键值对中,进行随机删除。
  4. volatile-lru 策略,会使用 LRU 算法筛选设置了过期时间的键值对。最近最少使用的会被删掉。
  5. volatile-lfu 会使用 LFU 算法选择设置了过期时间的键值对。首先会筛选并淘汰访问次数少的数据,然后针对访问次数相同的数据,再筛选并淘汰访问时间最久远的数据。
  6. allkeys-random 策略,从所有键值对中随机选择并删除数据。
  7. allkeys-lru 策略,使用 LRU 算法在所有数据中进行筛选。
  8. allkeys-lfu 策略,使用 LFU 算法在所有数据中进行筛选。
六、总结

欢迎评论区交流 ~

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值