redis补充 —— 缓存雪崩、缓存击穿、缓存穿透

前提:通常我们为了保证缓存中的数据与数据库中的数据一致性,会给 Redis 里的数据设置过期时间,当缓存数据过期后,用户访问的数据如果不在缓存里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到 Redis 里,这样后续请求都可以直接命中缓存。

缓存雪崩

大量缓存数据在同一时间(失效)或者Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是全部请求都直接访问数据库,从而导致数据库的压力剧增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩的问题。

发生缓存雪崩有两个原因:

  • 大量的缓存数据同时过期(失效)
  • redis 服务宕机

针对大量数据同时过期而引发的缓存雪崩问题,常见的应对方法有下面这几种:

  • 随机设置过期时间(TTL)
  • 互斥锁
  • 双 key 策略
  • 后台更新缓存

1. 随机设置过期时间

在给缓存数据设置过期时间时,可以给这些数据的过期时间加上一个随机数,这样就保证数据不会在同一时间过期。

2. 互斥锁 (但这种方案可能会影响并发量。)

当业务线程在处理用户请求时,如果发现访问的数据不在Redis里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到Redis里),当缓存构建完成后,再 释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。

实现互斥锁的时候,最好设置超时时间,不然第一个请求拿到了锁,然后这个请求发生了某种意外而一直阻塞,一直不释放锁,这时其他请求也一直拿不到锁,整个系统就会出现无响应的现象。

3. 双 key 策略

主 key 设置过期时间,备 key 不设置过期时间,当主 key 失效时,直接返回备 key 值。然后在更新缓存时,同时更新主 key 和备 key 的数据。

4. 后台更新缓存

业务线程不再负责更新缓存,缓存也不设置有效期,而是让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新

Redis 故障宕机
针对 Redis 故障宕机而引发的缓存雪崩问题,常见的应对方法有下面这几种:

服务熔断或请求限流机制;
构建 Redis 缓存高可靠集群;


1. 服务熔断或请求限流机制

因为 Redis 故障宕机而导致缓存雪崩问题时,我们可以启动服务熔断机制,暂停业务应用对缓存服务的访问,直接返回错误,不用再继续访问数据库,从而降低对数据库的访问压力,保证数据库系统的正常运行,然后等到 Redis 恢复正常后,再允许业务应用访问缓存服务。

服务熔断机制是保护数据库的正常允许,但是暂停了业务应用访问缓存服系统,全部业务都无法正常工作

为了减少对业务的影响,我们可以启用请求限流机制,只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。

2. 构建 Redis 缓存高可靠集群

服务熔断或请求限流机制是缓存雪崩发生后的应对方案,我们最好通过主从节点的方式构建 Redis 缓存高可靠集群。

如果 Redis 缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务,避免了由于 Redis 故障宕机而导致的缓存雪崩问题。


缓存击穿

缓存击穿也称(热点key问题),一个被高并发访问缓存业务重建困难的key突然过期(失效)了,于是全部请求都直接访问数据库,从而导致数据库的压力骤增甚至宕机。

如:tb秒杀活动,wb热榜等

 从定义上可以看出,缓存击穿和缓存雪崩很类似,只不过是缓存击穿是一个热点key失效,而缓存雪崩是大量热点key失效。因此,可以将缓存击穿看作是缓存雪崩的一个子集。

 缓存击穿的解决方案:

  • 使用互斥锁(Mutex Key),只让一个线程构建缓存,其他线程等待构建缓存执行完毕,重新从缓存中获取数据。单机通过synchronized或lock来处理,分布式环境采用分布式锁。
  • 热点数据不设置过期时间,后台异步更新缓存,适用于不严格要求缓存一致性的场景。
  • “提前”使用互斥锁(Mutex Key):在value内部设置一个比缓存(Redis)过期时间短的过期时间标识,当异步线程发现该值快过期时,马上延长内置的这个时间,并重新从数据库加载数据,设置到缓存中去。

缓存穿透

缓存穿透(cache penetration)是用户访问的数据既不在缓存当中,也不在数据库中。出于容错的考虑,如果从底层数据库查询不到数据,则不写入缓存。这就导致每次请求都会到底层数据库进行查询,缓存也失去了意义。当高并发或有人利用不存在的Key频繁攻击时,数据库的压力骤增,甚至崩溃,这就是缓存穿透问题。
缓存穿透发生的场景一般有两类:

  • 原本数据是存在的,但由于某些原因(误删除、主动清理等)在缓存和数据库层面都被删除了,但前端或前置的应用程序依旧保有这些数据;
  • 恶意攻击行为,利用不存在的key或者恶意尝试导致产生大量不存在的业务数据请求。

缓存穿透通常有四种解决方案:

方案一:缓存空值(null)或默认值

分析业务请求,如果是正常业务请求时发生缓存穿透现象,可针对相应的业务数据,在数据库查询不存在时,将其缓存为空值(null)或默认值。需要注意的是,针对空值的缓存失效时间不宜过长,一般设置为5分钟之内。当数据库被写入或更新该key的新数据时,缓存必须同时被刷新,避免数据不一致。

方案二:业务逻辑前置校验

在业务请求的入口处进行数据合法性校验,检查请求参数是否合理、是否包含非法值、是否恶意请求等,提前有效阻断非法请求。比如,根据年龄查询时,请求的年龄为-10岁,这显然是不合法的请求参数,直接在参数校验时进行判断返回。

方案三:使用布隆过滤器请求白名单

在写入数据时,使用布隆过滤器进行标记(相当于设置白名单),业务请求发现缓存中无对应数据时,可先通过查询布隆过滤器判断数据是否在白名单内,如果不在白名单内,则直接返回空或失败。

方案四:用户黑名单限制

当发生异常情况时,实时监控访问的对象和数据,分析用户行为,针对故意请求、爬虫或攻击者,进行特定用户的限制;

当然,可能针对缓存穿透的情况,也有可能是其他的原因引起,可以针对具体情况,采用对应的措施。
 

小结:

使用缓存时经常会遇到的三种异常情况:缓存雪崩、缓存击穿和缓存穿透。

三种异常情况从根本上来说都是因为本应该访问缓存的,但是缓存不存在或服务器异常,导致流量直接进入了数据库层面。

其中缓存雪崩和缓存击穿是因为缓存过期(或服务器异常获取不到),导致大量请求访问数据库,从而导致数据库压力骤增,甚至崩溃。

而缓存穿透则是数据本身就不存在,导致缓存没有进行数据缓存,流量进入数据库层。

针对不同的缓存异常场景,可选择不同的方案来进行处理。当然,除了上述方案,我们还可以限流、降级、熔断等服务层的措施,也可以考虑数据库层是否可以进行横向扩展,当缓存异常发生时,确保数据库能够抗住流量,不至于让整个系统崩溃。

参考文章:

 http://t.csdn.cn/DVN6H

 http://t.csdn.cn/G01pJ

http://t.csdn.cn/6Vrgt

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值