Mysql与Redis如何保证数据的一致性?

问题分析:

当MySQL中的数据发生更新时,就面临一个问题,如何确保MySQL与Redis数据的一致性,我们有两个选择:

  1. 先更新MySQL,后删除(或更新)Redis
  2. 先删除(或更新)Redis,后更新MySQL

但是不管使用其中哪种方式,都存在两个可能的问题:

  1. 由于第一步与第二步并不是原子的,中间会存在较短的时间间隔,如果间隔时间内有请求到达,就可能会访问到不一致的数据。
  2. 可能存在做完第一步,第二步还没来得及做,系统就发生异常的情况;这就会导致MySQL与Redis的数据不一致。

解决方案:

1、最终一致性:容忍数据短时不一致,最终达到一致即可

可以采用先删除Redis,后更新MySQL的方式来保证最终一致性。

  1. 删除Redis
  2. 在这时有请求到达,读取MySQL内容并写入Redis(此时写入的数据是旧的)
  3. 更新MySQL
  4. 删除Redis(再执行一次删除Redis,就可以确保Redis中的内容为最新数据)
    image
    但是上述的保证事务提交完以后再进行删除缓存还有一个问题,就是如果你使用的是 Mysql 的读写分离的架构的话,那么其实主从同步之间也会有时间差。
    image

此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)

  1. 请求 A 更新操作,删除了 Redis
  2. 请求主库进行更新操作,主库与从库进行同步数据的操作
  3. 请 B 查询操作,发现 Redis 中没有数据
  4. 去从库中拿去数据
  5. 此时同步数据还未完成,拿到的数据是旧数据

此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进行查询。
image

2、强一致性:数据实时保持一致

先更新数据库,后删除缓存
问题:更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。

此时解决方案就是利用消息队列进行删除的补偿。
image
具体的业务逻辑用语言描述如下:

  1. 请求 A 先对数据库进行更新操作
  2. 在对 Redis 进行删除操作的时候发现报错,删除失败
  3. 此时将Redis 的 key 作为消息体发送到消息队列中
  4. 系统接收到消息队列发送的消息后再次对 Redis 进行删除操作

但是这个方案会有一个缺点就是会对业务代码造成大量的侵入,深深的耦合在一起。
所以这时会有一个优化的方案,引入第三方服务,订阅(监听)MySQL master节点的binlog,当binlog产生时,按binlog顺序更新Redis数据即可。比如阿里开源的canal组件。
image

个人认为这里实现的【强一致性】也并不是真的【实时一致】,而是【近实时】,因为读取binlog并在Redis上执行更新,依然需要时间,只是这个时间会比较短而已。

posted @ 2022-11-28 13:15  盗梦笔记  阅读(3244)  评论(0编辑  收藏  举报