当前位置:   article > 正文

【数据库与缓存保持一致性】_数据库和缓存一致性如何保证

数据库和缓存一致性如何保证

1. 方案1

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

在这里插入图片描述

先更新缓存,在更新数据库

在这里插入图片描述

总结:无论是「先更新数据库,再更新缓存」,还是「先更新缓存,再更新数据库」,这两个方案都存在并发问题,当两个请求并发更新同一条数据的时候,可能会出现缓存和数据库中的数据不一致的现象。

  • 我们的业务对缓存命中率有很高的要求,我们可以采用「更新数据库 + 更新缓存」的方案,因为更新缓存并不会出现缓存未命中的情况。

问题

  • 在两个更新请求并发执行的时候,会出现数据不一致的问题,因为更新数据库和更新缓存这两个操作是独立的,而我们>又没有对操作做任何并发控制,那么当两个线程并发更新它们的话,就会因为写入顺序的不同造成数据的不一致。

解决办法

  1. 在更新缓存前先加个分布式锁,保证同一时间只运行一个请求更新缓存,就会不会产生并发问题了,当然引入了锁后,对于写入的性能就>会带来影响。
  2. 在更新完缓存时,给缓存加上较短的过期时间,这样即时出现缓存不一致的情况,缓存的数据也会很快过期,对业务还是能接受的。

2. 方案2

  • 先更新数据库,还是先删除缓存,叫Cache Aside 策略,中文是叫旁路缓存策略

  • 该策略又可以细分为「读策略」「写策略」

  • 写策略的步骤

    1. 更新数据库中的数据;
    2. 删除缓存中的数据。
  • 读策略的步骤

    1. 如果读取的数据命中了缓存,则直接返回数据;
    2. 如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。

先更新数据库,在删缓存

在这里插入图片描述

  • 因为缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现请求 B 已经更新了数据库并且删除了缓存,请求 A 才更新完缓存的情况。
  • 而一旦请求 A 早于请求 B 删除缓存之前更新了缓存,那么接下来的请求就会因为缓存不命中而从数据库中重新读取数据,所以不会出现这种不一致的情况。
  • 缓存数据加上了「过期时间」,就算在这期间存在缓存数据不一致,有过期时间来兜底,这样也能达到最终一致。

所以,「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。

问题

  • 明明更新了数据,但是数据要过一段时间才生效。(原因:「先更新数据库, 再删除缓存」其实是两个操作,前面的所有分析都是建立在这两个操作都能同时执行成功,而这次客户投诉的问题就在于,在删除缓存(第二个操作)的时候失败了,导致缓存中的数据是旧值。给缓存加上了过期时间,所以才会出现客户说的过一段时间才更新生效的现象。)

先删缓存,在更新数据库

在这里插入图片描述

解决办法

  • 延迟双删
#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 加个睡眠时间,主要是为了确保请求 A 在睡眠的时候,请求 B 能够在这这一段时间完成「从数据库读取数据,再把缺失的缓存写入缓存」的操作,然后请求 A 睡眠完,再删除缓存。
    所以,请求 A 的睡眠时间就需要大于请求 B 「从数据库读取数据 + 写入缓存」的时间。

建议使用「先更新数据库,再删除缓存」的方案。

3. 方案3—如何保证两个操作都能执行成功?

  • 「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。但是在删除缓存(第二个操作)的时候失败了,导致缓存还是旧值,而数据库是最新值,虽然加了过期时间,但是也需要过一会才生效,所以还会造成数据库和缓存数据不一致的问题。
  • 不管是先操作数据库,还是先操作缓存,只要第二个操作失败都会出现数据一致的问题。
    在这里插入图片描述
    解决办法
  • 重试机制。
  • 订阅 MySQL binlog,再操作缓存。

重试机制

  • 引入消息队列,将第二个操作(删除缓存)要操作的数据加入到消息队列,由消费者来操作数据。
    • 如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。当然,如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
    • 如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作,否则就继续重试。

在这里插入图片描述

订阅 MySQL binlog

  • 「先更新数据库,再删缓存」的策略的第一步是更新数据库,那么更新数据库成功,就会产生一条变更日志,记录在 binlog 里。
  • 通过订阅 binlog 日志,拿到具体要操作的数据,然后再执行缓存删除,Canal 中间件就是基于这个实现的。

Canal 原理

  • Canal 模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 的从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后,就会开始推送 Binlog 给 Canal,Canal 解析 Binlog 字节流之后,转换为便于读取的结构化数据,供下游程序订阅使用。
    在这里插入图片描述
    文章:https://www.xiaolincoding.com/
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/466953
推荐阅读
相关标签
  

闽ICP备14008679号