引入缓存竟然会带来这么多问题?( 三 )


一般解决redis故障宕机,是搭建集群,由单节点到多节点,提升redis的容灾能力,当主节点宕机后,从节点可以切换成为主节点,继续提供缓存服务;若是真的宕机了,那我们应该使用熔断机制 , 同时当流量到达一定的阈值,直接禁止请求对数据库的访问,返回系统拥挤之类的提示,维持系统稳定 , 等待缓存恢复再允许对数据库访问
防止大量缓存数据在同一时间过期,一般是给缓存的Key设置合理的过期时间并加上随机偏差,尽量让缓存失效时间均匀分布,实现"削峰填谷",简单而有效

引入缓存竟然会带来这么多问题?

文章插图
图片
要么加锁,唯一db请求,所有同类请求共享结果,与缓存击穿的解决方法一致 , 我们就不再赘述了
还有一种方式就是,当每天系统访问的流量高峰来临之前,先提前将热点数据入缓存 , 避免直到用户请求的时候,再先查询数据库,然后将数据缓存的过程,这个也叫缓存预热
CAP原则 和 如何保证缓存一致性由于在数据库层前,引入缓存,主要是通过空间去换时间,享受缓存带来的种种好处的优点,但此时一份数据存在不同的副本,且在不同空间中,此时更新缓存、db就会带来缓存一致性的挑战
我们还需要了解一下著名的CAP原则,指在一个分布式系统中,一致性Consistency、可用性AvAIlability、分区容错性Partition tolerance,这3者最多同时满足2项 , 不可能同时满足3项?。。?
引入缓存竟然会带来这么多问题?

文章插图
图片
  1. 一致性Consistency,即所有节点在同一时间具有相同的数据,强一致性
  2. 可用性Availability,即服务必须一直处于可用的状态,每次请求都能获取到正常的响应,高可用
  3. 分区容错性Partition tolerance,即分区故障时 , 要求在一定时限内,仍然或者恢复到能对外提供满足一致性和可用性的服务,系统继续正常运行
还记得本文的一开始吗?
为了应对高流量,我们的系统选择了高性能和高吞吐量,所以只能满足AP 。
而缓存与数据库的缓存一致性难以避免的具体原因是:由于无法保证同时更新db和缓存不在同一个事务中,所以其不是原子操作,缓存不一致是无法避免的!
引入缓存竟然会带来这么多问题?

文章插图
图片
要保证强一致性,我们可以上分布式锁,但会导致整个系统的并发性能下降 , 还记得我们引入缓存的初衷吗?是为了提升系统的整体性能呐?。。∷?哉庵址桨肝颐且话悴徊捎脋
但我们可以通过一些方案,来实现缓存的最终一致性,其次尽可能减小缓存不一致的时间窗口 , 我们下面分别来聊聊常见的几种方式及其它们的问题:
  1. 先更新数据库,再更新缓存
  2. 先更新缓存,再更新数据库
  3. 先删缓存,再更新数据库
  4. 先更新数据库,再删除缓存
先更新数据库 , 再更新缓存先更新数据库,再更新缓存,可能会遇到下面这种情况:
引入缓存竟然会带来这么多问题?

文章插图
图片
当请求(或者可以说线程)并发的情况 , 比如2个请求1、2同时去更新db时,请求1快一点;但当程序延迟或者其他情况,导致当请求去更新缓存时,请求2快一点,这就会导致最终db=20,缓存=10这种数据不一致的情况,不一致的情况将持续到下次缓存失效 , 或者去更新数据库缓存的时候,在此期间还不能保证更新缓存一定就可以成功
先更新缓存,再更新数据库这种和先更新数据库,再更新缓存是类似的情况:
引入缓存竟然会带来这么多问题?

文章插图
图片
这种更新缓存的方式,是无法避免并发导致的数据不一致问题 , 而且出现的频率也不低,所以我们应该尽量不更新缓存 。
先删缓存 , 再更新数据库 和 延迟双删前一个更新请求,先删除缓存,再更新数据库,当后面读请求来发现没有命中缓存,去数据库读数据,然后再回写到缓存中,给后续请求服务 , 这是个很不错的设想,但它还是会出现下面这种情况:


推荐阅读