redis之缓存穿透,缓存击穿,缓存雪崩区别及解决方案

缓存穿透

  • 问题形成

缓存穿透是指查询一个一定不存在的数据,由于缓存不命中,接着查询数据库也无法查询出结果,因此也不会写入到缓存中,这将会导致每个查询都会去请求数据库,造成缓存穿透;
一般的缓存系统,都是按照key值去缓存查询,如果不存在对应的value,就应该去DB中查找 。这个时候,如果请求的并发量很大,就会对后端的DB系统造成很大的压力。这就叫做缓存穿透。关键词:缓存value为空;并发量很大去访问DB。
在这里插入图片描述

  • 造成原因
    • 业务自身代码或数据出现问题;
    • 一些恶意攻击,爬虫造成大量空的命中,此时会对数据库造成很大压力。
  • 解决方案
    • 布隆过滤

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

【设置布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从避免了对底层存储系统的查询压力。】
在这里插入图片描述

  • 缓存空对象

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

如果一个查询返回的数据为空,不管是数据不存在还是系统故障,我们仍然把这个结果进行缓存,但是它的过期时间会很短最长不超过5分钟。
在这里插入图片描述
但是这种方法会存在两个问题:

  1. 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
  2. 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
比较

在这里插入图片描述

缓存穿透

  • 缓存穿透,是指查询一个数据库一定不存在的数据。一般情况下redis不会有缓存,就会去访问数据库,大量访问造成数据库压力大从而崩溃。
  • 解决方式和缓存穿透一致

缓存雪崩

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

因为缓存层承载了大量的请求,有效的保护了存储 层,但是如果缓存由于某些原因,整体不能够提供服务,于是所有的请求,就会到达存储层,存储层的调用量就会暴增,造成存储层也会挂掉的情况。缓存雪崩的英文解释是奔逃的野牛,指的是缓存层宕掉之后,并发流量会像奔腾的野牛一样,大量访问后端存储。

存在这种问题的一个场景是:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,大量数据会去直接访问DB,此时给DB很大的压力。
在这里插入图片描述

  • 解决方案

    • 保持缓存的高可用性
      使用redis哨兵模式或者redis集群部署方式,即便个别的redis节点下线,整个缓存层依然可以使用。除此之外,还可以在多个机房部署redis,这样即便是机房死机,依然可以实现缓存层的高可用。
    • 限流降级组件
      无论是缓存层还是存储层都会有出错的概率,可以将它们视为资源。作为并发量较大的分布式系统,假如有一个资源不可用,可能会造成所有线程在获取这个资源时异常,造成整个系统不可用。降低在高并发系统中非常正常的,比如推荐服务器中,比如个性化推荐服务不可用,可以降级补充热点数据,不至于造成整个推荐服务不可用。创建的限流降级组件如Hystrix、Sentinel等。
  • 缓存不过期

redis中保存的key永不失效,这样就不会出现大量缓存同时失效的问题,但是随之而来的就是redis需要更多存储空间。

优化缓存过期时间
设计缓存时候,为每一个key选择合适的过期时间,避免大量的key在同一时刻,同时失效,造成缓存雪崩。

  • 使用互斥锁重建缓存
    在高并发场景下,为了避免大量的请求同时达到存储层查询数据,重建缓存,可以使用互斥锁控制,如根据key去缓存层查询数据,当缓存层为命中时,对key加锁,然后从存储层查询数据,将数据写入缓存层,最后释放锁,若其他线程发现获取锁失败,则让线程休眠,一段时间后重试。对于锁的类型,如果是在单机环境下可以使用java并发包下的Lock,如果是在分布式环境下,可以使用分布式锁(Redis中的SETNX方法)。

分布式环境下使用redis分布式锁实现缓存重建,优点是设计思路简单,对数据一致性有保障,缺点是代码复杂度增加,有肯能造成用户等待,假设在高并发下,缓存重建期间key是锁着的,如果当前并发1000个请求,其中999个都在阻塞,会导致999个用户请求阻塞而等待。

在缓存失效后,通过加锁或者队列来控制读和写数据库的线程数量。比如:对某个key只允许一个线程查询数据和写缓存,其他线程等待。单机的话,可以使用synchronized或者lock来解决,如果是分布式环境,可以是用redis的setnx命令来解决。

  • 异步重建缓存
    这种方案下构建缓存采取异步策略,会从线程池中获取线程来异步构建缓存,从而不会让所有的请求直接到达存储层,该方案中redis key 维护逻辑超时时间,档逻辑超时时间小于当前时间时,则说明缓存已经失效,应当进行缓存更新,否则说明当前缓存未失效,直接返回缓存中的value值。如在redis中将key的过期时间设置为60min,在对应的value中设置逻辑过期时间为30min。这样key到了30min 的逻辑过期时间,就可以异步更新这个key的缓存,但是在更新缓存的这段时间内,旧的缓存依然可以使用,这种异步重建缓存的方式可以有效避免大量的key同时失效。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值