REDIS21_缓存双写一致方案、先更新数据库再删除缓存

①. 什么是缓存双写一致

  • ①. 缓存双写一致性,谈谈你的理解
  1. 如果redis中有数据,需要和数据库中的值相同
  2. 如果redis中无数据,数据库中的值要是最新值
  • ②. 什么时候同步直写?
    小数据,某条、某一小戳热点数据,要求立刻变更,可以前台服务降价一下,后台马上同步直写

  • ③. 什么时候异步缓写?

  1. 正常业务,马上更新mysql,可以在业务上容许出现1个小时后redis起效
  2. 出现异常后,不得不将失败的动作重新修补,不得不借助Kafka或者RabbitMQ等消息中间件,实现解耦后重试重写

②. 先更新数据库,再更新缓存

  • ①. 先更新mysql的某商品的库存,当前商品的库存是100,更新为99个

  • ②. 先更新mysql修改为99成功,然后更新redis

  • ③. 此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100

  • ④. 上述发生,会让数据库里面和缓存redis里面数据不一致,读到脏数据

③. 先删除缓存,再更新数据库

  • ①. A线程先成功删除了redis里面的数据,然后去更新mysql,此时mysql正在更新中,还没有结束。(比如网络延时)B突然出现要来读取缓存数据

  • ②. 此时redis里面的数据是空的,B线程来读取,先去读redis里数据(已经被A线程delete掉了),此处出来2个问题:

  1. B从mysql获得了旧值:B线程发现redis里没有(缓存缺失)马上去mysql里面读取,从数据库里面读取来的是旧值
  2. B会把获得的旧值写回redis:获得旧值数据后返回前台并回写进redis(刚被A线程删除的旧数据有极大可能又被写回了)
  • ③. A线程更新完mysql,发现redis里面的缓存是脏数据

  • ④. 总结流程

  1. 请求A进行写操作,删除缓存后,工作正在进行中…A还么有彻底更新完
  2. 请求B开工,查询redis发现缓存不存在
  3. 请求B继续,去数据库查询得到了myslq中的旧值
  4. 请求B将旧值写入redis缓存
  5. 请求B将旧值写入redis缓存
    在这里插入图片描述
  • ⑤. 采用延时双删策略:
    加上sleep的这段时间,就是为了让线程B能够先从数据库读取数据,再把缺失的数据写入缓存,然后,线程A再进行删除。所以,线程A Sleep的时间,就需要大于现场B读取数据再写入缓存的时间。这样一来,其他线程读取数据时,会发生缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再去进行删除,所以我们也把它叫做"延迟双删"
    在这里插入图片描述
  • ⑥. 这个删除该休眠多久呢?这个时间怎么确定呢?
  1. 在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可
  2. 这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据
  • ⑦. 这种同步淘汰策略,吞吐量降低怎么办?异步删除
    在这里插入图片描述

④. 先更新数据库,再删除缓存

  • ①. 异常原因:假如缓存删除失败或者来不及,导致请求再次访问redis时缓存命中,读取到的是缓存旧值
    在这里插入图片描述
  • ②. 解决方案
  1. 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)
  2. 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新
  3. 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
  4. 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员
    在这里插入图片描述
  • ③. 谈谈为什么要引入MQ
  1. 在应用程序将数据更新到数据库后,将更新操作发送到消息队列中,然后再由消息队列异步地触发删除缓存数据的操作
  2. 这样做的好处是,即使在更新数据库后发生异常或者网络延迟等问题,数据更新操作也已经被放到消息队列中,并不会导致缓存数据和数据库数据不一致的问题

猜你喜欢

转载自blog.csdn.net/TZ845195485/article/details/129913151