Redis缓存穿透、缓存血崩、缓存击穿的原因和解决方案

缓存穿透

指查询一条根本不存在的数据,缓存和持久层都不会命中。 通常出于容错考虑,数据库不存在的数据不会写入缓存,缓存穿透将导致每次查询不存在的数据,都会穿过缓存,去查询持久层,从而失去了缓存保护持久层的作用。
下图为缓存穿透示意图:
缓存穿透示意图
缓存穿透问题可能会使后端存储负载加大,由于很多后端持久层不具备高并发性,甚至可能造成后端存储宕机。通常可以在程序中统计总调用数、缓存层命中数、如果同一个Key的缓存命中率很低,可能就是出现了缓存穿透问题。
一般造成缓存穿透的基本原因有两个。第一,自身业务代码或者数据出现问题;第二,一些恶意攻击、爬虫等造成大量空命中(爬取线上商城商品数据,超大循环递增商品的ID)

解决方案

1.缓存空对象
指数据库没有命中(查询返回空)时,进行缓存(key, null)。
缓存空对象会有两个问题:

  • 占用更多的缓存空间,此时可设置较短的过期时间。
  • 若缓存存在的期间添加了该数据,可能会存在数据不一致问题,可通过消息系统或异步清除或更新缓存。

2.布隆过滤拦截器
在访问缓存之前,将存在的key用布隆过滤器提前保存起来,做第一层拦截,当收到一个对key请求时先用布隆过滤器验证是key否存在,如果存在在进入缓存层、存储层。可以使用BitSet做布隆过滤器。这种方法适用于数据命中不高、数据相对固定、实时性低的应用场景,代码维护较为复杂,但是缓存空间占用少。
优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
算法描述:

  • 初始状态时,BloomFilter是一个长度为m的位数组,每一位都置为0。
  • 添加元素x时,x使用k个hash函数得到k个hash值,对m取余,对应的bit位设置为1。
  • 判断y是否属于这个集合,对y使用k个哈希函数得到k个哈希值,对m取余,所有对应的位置都是1,则认为y属于该集合(哈希冲突,可能存在误判),否则就认为y不属于该集合。可以通过增加哈希函数和增加二进制位数组的长度来降低错报率。

错报原因:
一个key映射数组上多位,一位会被多个key使用,也就是多对多的关系。如果一个key映射的所有位值为1,就判断为存在。但是可能会出现key1 和 key2 同时映射到下标为100的位,key1不存在,key2存在,这种情况下会发生错误率。

方案对比

缓存血崩

指缓存由于某些原因不可用(如宕机)或大量缓存由于有效时间相同,在同一段时间内失效(大批热点数据失效),而导致大量请求到达持久层,从而致使持久层无法承受过大压力而系统血崩。

解决方案

  1. 可以把缓存层设计成高可用的,即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务。利用sentinel或cluster实现。
  2. 采用多级缓存,本地进程作为一级缓存,redis作为二级缓存,不同级别的缓存设置的超时时间不同,即使某级缓存过期了,也有其他级别缓存兜底。
  3. 缓存的过期时间用随机值,尽量让不同的key的过期时间不同(例如:定时任务新建大批量key,设置的过期时间相同)。

缓存击穿

指一个热点key(如秒杀)在缓存失效的瞬间,大量线程访问并重建缓存,造成后端压力过大,甚至可能让应用崩溃。
系统中存在以下情况时需要注意:

  1. 存在并发量特别大的热点key(如秒杀活动);
  2. 重建缓存不能在短时间内完成时,如复杂计算,复杂的SQL,多次IO,多个依赖等。

解决方案

1.分布式互斥锁
只允许一个线程重建缓存,其他线程等待缓存重建完成,直接从缓存获取数据。

2.永不过期
物理不过期,逻辑过期。每个key实际上的缓存是没有过期时间,但是维护一个逻辑过期时间和一个是否已开始重建缓存的标志。当线程进来时,先判断逻辑过期时间,若已过期,再判断是否已开始重建,若未开始重建,则修改是否已开始重建的标志,并异步去重建缓存,然后返回当前缓存中的值;否则也是直接返回当前缓存中的值。如下图

2种方案对比:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值