缓存穿透、缓存击穿和缓存雪崩

基本的业务架构

当客户端发送请求时,业务服务会先去 Redis 缓存中查询,判断数据是否在缓存中。如果数据不在缓存中,就去 MySQL 数据库层查询,然后把查询到的数据,缓存到 Redis 中,再把结果返回给客户端。

当下一次请求同样的数据时,就可以直接从 Redis 缓存中获取数据,避免了再去 MySQL 数据库层查询,从而减轻底层数据库的压力。这在很大程度上提升了程序的响应速度。

缓存穿透

当数据查询在 Redis 缓存中没有数据时,该查询会下沉到 MySQL 数据库层,同时如果数据库层也没有该数据,就无法进行缓存。当出现大量这种查询(或被恶意攻击)时,查询请求全部透过缓存访问底层数据库,而数据库中也没有这些数据,这种现象就是缓存穿透

缓存穿透会穿透 Redis 缓存的保护,让底层数据库的负载压力变大。

对于缓存穿透,至少有以下两种解决方案。

解决方案一

在业务服务访问层对请求进行校验,判断是否来自恶意用户(比如爬虫、超大循环递增商品的 id)的请求。比如对请求参数进行校验和检查一段时间内请求同一个服务的次数。

解决方案二

采用布隆过滤器进行拦截。将数据库层有关数据的键提前存入布隆过滤器中,用来判断访问的键是否在底层数据库中。

可以将布隆过滤器的判断逻辑,置于查询 Redis 缓存之前。请求在查询 Redis 缓存之前,先判断请求的 key 是否存在于布隆过滤器中。如果在,就查询缓存,如果缓存中没有,才去查询数据库;如果不在布隆过滤器中,就直接返回。

当然,也可以把布隆过滤器的层级放到数据库的上一层。请求数据时,先去缓存查询。如果缓存里有,就直接返回结果;如果缓存里没有,就先判断是否在布隆过滤器中。如果在,才允许查询 MySQL 数据库,如果不在布隆过滤器中,就直接返回。

通过前置一层布隆过滤器,可以将绝大部分的穿透查询屏蔽在 Redis 层,极大地降低了底层数据库的压力。

缓存击穿

热点数据在某个时间点过期,恰好在这个时间点,大量并发请求前来查询这些 key ,就会将查询请求下沉到数据库层,导致数据库层的负载压力增大,这种现象就是缓存击穿

热点数据(比如秒杀活动的数据)一般会有下面的特征:

  • 并发量非常大
  • 重建缓存无法在短时间内完成,可能需要很多次 IO、复杂的 SQL 查询等

对于缓存击穿,至少有以下两种解决方案。

解决方案一

热点数据的过期时间设为永不过期。

另一种思路是:对于有过期时间的 key,我们可以在对应的 value 上加一个逻辑过期时间(logic_expire),逻辑过期时间必须小于 key 的过期时间,当发现当前时间超过逻辑过期时间时,就采用异步的方式更新缓存。

解决方案二

利用互斥锁(mutex lock)保证同一时刻只有一个客户端可以查询底层数据库和重建缓存。

对于热点 key,查询缓存时若没有数据,先不要查询数据库,而是使用 setnx 和 expire,来保证只会有一个请求争抢到锁,抢到锁后才去查询数据库和重建缓存。其他没有抢到锁的请求,等待重试,重新查询缓存即可。

大多数系统设计者考虑用加锁或队列的方式来保证缓存的单线程写,从而避免失效时大量的并发请求落到底层数据库。

互斥锁是业界比较常用的做法。

缓存雪崩

大面积的缓存数据几乎同时过期,之后,大量并发的查询穿过 Redis,直接冲击底层数据库,导致数据库层的负载压力激增。这种现象就是缓存雪崩

相比于缓存击穿,缓存雪崩更容易发生。

对于缓存雪崩,至少有以下两种解决方案。

解决方案一

根据实际情况,将缓存数据的过期时间设为永不过期。

另一种思路是:对于有过期时间的 key,我们可以在对应的 value 上加一个逻辑过期时间(logic_expire),逻辑过期时间必须小于 key 的过期时间,当发现当前时间超过逻辑过期时间时,就采用异步的方式更新缓存。

解决方案二

同缓存击穿的解决方案二。

解决方案三

在一定的时间范围内,均匀分散设置键的过期时间,来防止大量的键在同一时刻过期。

比如,之前一些键的过期时间是 1 个小时,现在就可以设置为不同的时间,比如 1 小时 1 分钟、1小时 2 分钟等。可根据业务情况选择不同的随机因子。

解决方案四

采用多级缓存策略。不同级别的缓存设置的过期时间不同,即使某一级缓存过期了,也会有其他级别的缓存兜底。

区别

  • 缓存穿透:缓存和底层数据库中都没有数据,无法进行缓存。
  • 缓存击穿:热点数据过期,缓存中没有数据,但底层数据库中有数据。
  • 缓存雪崩:大面积的缓存数据同时过期,缓存中没有数据,但底层数据库中有数据。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值