Redis应用缓存穿透、缓存击穿、缓存雪崩问题的原因及解决方案

一、缓存穿透

1、原因

所谓缓存穿透,可以理解为请求DB中不存在的key对应的数据。

通常情况下,当请求redis中某个key时,如果redis中没有,会请求DB,若DB上有key对应的数据,那么就会将这条数据缓存到redis中。但是当DB上也没有key对应的数据时,出于容错考虑,不会回写DB中不存在的数据到redis中。如果请求DB中不存在的key的并发量很小的时候还好,如果并发量很大,那么大量的请求会全部怼到DB上,从而可能会压垮DB。

2、解决方案

  1. 对空值缓存:如果查询DB返回的结果为空(不管数据是否不存在),仍将这个空结果(null)进行缓存,并设置较短的过期时间,最长不超过5分钟。但这个方案有个问题,若有人恶意请求大量DB中不存在的数据,那么redis中将会缓存大量值为null的key,会占用大量内存,并且一些有用的缓存数据可能会被redis的内存淘汰策略淘汰掉。
  2. 设置白名单:提供一个能迅速判断请求是否有效的拦截机制。比如,使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问,请求的key和bitmap里面的id进行比较,如果该key不在bitmaps里面,进行拦截,不允许访问。可以把这种方式理解为二级缓存,bitmaps作为一级缓存,key-value作为二级缓存。这种方式也会减少redis的压力。
  3. 使用布隆过滤器:布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
  4. 进行实时监控当发现redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

二、缓存击穿

1、原因

        所谓缓存击穿,可以理解为请求的某一个key不在redis中,但是DB中存在key对应的数据。(注意这里是某一个key

        通常我们在使用redis缓存数据的时候会给key设置过期时间,若某个key已过期,此时大量的并发请求访问该key,这些请求发现缓存过期一般都会从DB加载数据并回设到缓存,这些并发请求会造成DB压力过大甚至瞬间压垮DB。

2、解决方案

  1. 不设置过期时间:在业务允许的情况下,可以设置热点key永不过期。
  2. 实时调整:现场监控哪些数据热门,实时调整key的过期时长。
  3. 数据预热:在redis高峰访问之前,把一些热门数据提前存入到redis里面,把这些热门key的过期时间设置长一些。
  4. 使用分布式锁:当大量的并发请求访问redis的过期key时,这些并发请求会怼到DB上并回写redis。此时我们使用分布式锁(如redis的setnx操作),设置一个排他锁,只有获得到这个锁的线程才可以去DB上查询并回写redis,其他的请求等待一会再从redis中查询这个key,从而避免大量的并发请求DB。但是这种方式会降低系统的吞吐量,在高并发的场景下影响性能。注意,因为现在大部分项目是分布式的,即项目部署到多台服务器上,故java的锁不能满足这种情况。若是项目的单机部署的,也可以使用java锁。

三、缓存雪崩

1、原因

        所谓缓存雪崩,可以理解为请求的多个key不在redis中,但是DB中存在key对应的数据。(注意这里是多个key

        与缓存击穿类似, 通常我们在使用redis缓存数据的时候会给key设置过期时间,若同一时间大量缓存key大面积的失效,此时大量的并发请求访问这些key,这些请求发现缓存过期一般都会从DB加载数据并回设到缓存,这些并发请求会造成DB压力过大甚至瞬间压垮DB。与缓存击穿不同的是,缓存雪崩是大量的key过期,而缓存击穿是某一个key过期。此外,redis宕机,导致在同一时间大规模的key失效也会导致缓存雪崩。

        缓存失效时的雪崩效应对底层系统的冲剂是非常可怕的。

2、解决方案

  1. 构建redis集群:为防止redis宕机导致缓存雪崩,可以搭建redis集群,保障redis的高可用。
  2. 将缓存失效时间分散开:可以在原有过期时间的基础上增加一个随机值,比如1-5分钟随机。这样过期时间=基础过期时间+随机时间。这样每一个缓存的过期时间的重复率就会降低,避免大量key集体失效。
  3. 数据预热:在redis高峰访问之前,把一些热门数据提前存入到redis里面,设置不同的过期时间,让缓存失效的时间尽量不同。
  4. 异步更新策略设置过期标志,记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。比如redis中有个key为A,过期时间为5min,我们给A设置过期标志为B,B的过期时间要比A的小,比如B的过期时间为4min,当B失效时去更新A,并将A的过期时间仍设为5min,B的过期时间仍设为4min。
  5. 使用分布式锁或队列:使用分布式锁或者队列的方式来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到DB上。但是这方案吞吐量明显下降了,不适合高并发的场景。
  6. 构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)。这种方式会加大成本。
  7. 双缓存:我们有两个缓存,缓存A和缓存B。比如缓存A的失效时间为20min,缓存B不设失效时间。自己做缓存预热。然后细分以下几个小点:
          i)从缓存A读数据,有则直接返回;
          ii)缓存A中没有数据,直接从缓存B中读数据,直接返回,并且异步启动一个更新线程;
          iii)更新线程同时更新缓存A和缓存B。

四、总结

        缓存穿透就是没有命中redis,DB中也不存在;缓存击穿就是某个热点key失效;缓存雪崩就是大量key失效。我觉得我的好友凯少对redis的这三个问题比喻的很好:redis就像一堵墙,key就是一块块砖,雪崩就是整个墙都塌了或者部分塌了,击穿就是只有一块砖坏了,穿透就是请求可以遁入虚空,无视这堵墙。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于 Redis 缓存的穿透、击穿和雪崩问题,可以采取以下解决方案: 1. 缓存穿透:当请求的数据在缓存中不存在时,会直接访问数据库,如果有恶意攻击者大量请求不存在的数据,会给数据库造成很大压力。解决方案可以是在查询前进行参数校验,比如对请求的参数进行合法性检查,或者使用布隆过滤器等技术来快速判断请求的数据是否存在。 2. 缓存击穿:当某个热点数据过期或被删除时,大量请求同时涌入,导致请求直接访问数据库。为了解决这个问题,可以使用互斥锁(Mutex)或者分布式锁来避免多个请求同时访问数据库。在获取锁之前,首先尝试从缓存获取数据,如果缓存中存在,则直接返回;如果不存在,则获取锁,并从数据库中获取数据并放入缓存,最后释放锁。 3. 缓存雪崩:当缓存中的大量数据同时过期时,会导致大量请求直接访问数据库,给数据库带来巨大压力。为了解决这个问题,可以采取以下措施: - 设置合理的缓存过期时间,使得不同数据的过期时间错开,避免同时失效。 - 使用热点数据预加载,提前将热点数据加载到缓存中,避免同时失效。 - 使用多级缓存架构,将缓存分为多个层级,不同层级的缓存设置不同的过期时间,从而降低缓存失效的风险。 - 引入限流和熔断机制,对请求进行限制,避免大量请求同时访问数据库。 通过以上措施,可以有效地解决 Redis 缓存穿透、击穿和雪崩问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luffylv

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值