Redis面试题分享十:Redis 与 MySQL如何保证双写一致性?

目录

一、什么是数据一致性问题

二、四种读写缓存策略

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

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

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

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

三、解决问题策略思想

四、具体解决方案

1、延时双删

2、Binlog实现异步重试删除


一、什么是数据一致性问题

在这样一个架构中,会出现一个问题,就是一份数据,同时保存在数据库和Redis 里面,当数据发生变化的时候,需要同时更新 Redis 和 Mysql,由于更新是有先后顺序的,并且它不像 Mysql 中的多表事务操作,可以满足 ACID 特性。所以就会出现数据一致性问题。

Redis与MySQL双写一致性是指在使用缓存和数据库同时存储数据的场景下( 主要是存在高并发的情况)如何保证两者的数据一致性(内容相同或者尽可能接近)

二、四种读写缓存策略

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

线程A:更新缓存(第1s)——> 更新数据库(第10s)

线程B:更新缓存(第3s)——> 更新数据库(第5s)

缓存中是线程B的新值,而数据库中是线程A的旧值。

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

线程A:更新数据库(第1s)——> 更新缓存(第10s)

线程B:更新数据库 (第3s)——> 更新缓存(第5s)

并发场景下,这样的情况是很容易出现的,每个线程的操作先后顺序不同,这样就导致请求B的缓存值被请求A给覆盖了, 数据库中是线程B的新值,缓存中是线程A的旧值,并且会一直这么脏下去直到缓存失效(如果你设置了过期时间的话)。

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

通过这种方式,我们很惊喜地发现,前面困扰我们的并发场景的问题确实被解决了!两个线程都只修改数据库,不管谁先,数据库以之后修改的线程为准。

但这个时候,我们来思考另一个场景:两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的。很显然,这种状况也不是我们想要的。

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

这种方式,在方案3的基础上,又将二者的顺序进行了调换。我们再把前面的场景在这种方案下进行验证:一个是查询操作,一个是更新操作的并发,我们先更新了数据库中的数据,此时,缓存依然有效,所以,并发的查询操作拿的是没有更新的数据,但是,更新操作马上让缓存的失效了,后续的查询操作再把数据从数据库中拉出来。而不会方案3一样,后续的查询操作一直在取老的数据。

而这,也正是缓存使用的标准的design pattern,也就是cache aside。包括Facebook的论文《Scaling Memcache at Facebook》也使用了这个策略。

那么,是否这种方案就是万无一失的完美策略呢?其实也并不然,再来看看这种场景:一个是读操作,但是没有命中缓存,然后就到数据库中取数据,此时来了一个写操作,写完数据库后,让缓存失效,然后,之前的那个读操作再把老的数据放进去,所以,会造成脏数据。

因此,在使用更新缓存操作时,无论谁先谁后,但凡后者发生异常,就会对业务造成影响。

三、解决问题策略思想

  1. 选择合适的数据同步方式:根据应用的需求和数据访问模式,选择合适的数据同步方式。同步方式包括单向同步、双向同步和不同步等。
  2. 使用事务:在 MySQL 中使用事务来确保数据的原子性。在 Redis 中,可以使用 MULTI/EXEC 命令组来执行事务操作。
  3. 避免长时间的事务:MySQL 中的长时间事务可能会导致 Redis 中的数据不同步。因此,在设计数据库操作时,应尽量避免长时间的事务。
  4. 主从复制:使用 MySQL 的主从复制功能,将数据变更同步到从数据库。然后,使用 Redis 的订阅/发布功能,将从数据库中的数据变更同步到 Redis。这种方式可以实现 MySQL 和 Redis 的双向同步。
  5. 分布式事务:使用分布式事务协议,如两阶段提交或三阶段提交,来确保 Redis 和 MySQL 之间的数据一致性。分布式事务可以保证多个数据库之间的数据一致性。
  6. 定时同步:对于一些不需要实时同步的数据,可以使用定时同步的方式。例如,每天定时将 MySQL 中的数据同步到 Redis 中。
  7. 数据校验:定期进行数据校验,以确保 Redis 和 MySQL 中的数据一致。如果数据不一致,及时进行修复。

四、具体解决方案

1、延时双删

我们重点在将先更新数据库,在删除缓存。那如果我要先删除缓存,再更新数据库呢?
回顾之前讲的先删除缓存,再更新数据库,它会出现旧值覆盖缓存的问题,那好办,我们直接把这个旧值给删了不就完了吗,延时双删就是这个原理,它的基本思路是:

  • 先删除缓存
  • 再更新数据库
  • 休眠一段时间(根据系统情况确定)
  • 再次删除缓存

这样做的目的是为了防止在更新数据库后,有其他线程读取到旧的缓存数据,并将其写回缓存,导致数据不一致。

2、Binlog实现异步重试删除

使用binlog实现一致性的基本思路是利用binlog日志来记录数据库的变更操作,然后通过主从复制或者增量备份的方式来同步或者恢复数据。

举例来说,如果我们有一个主数据库和一个从数据库,我们可以在主数据库上开启binlog日志,并设置从数据库作为它的复制节点。这样,当主数据库上发生任何变更操作时,它会将对应的binlog日志发送给从数据库,从数据库则会根据binlog日志来执行相同的操作,从而保证数据一致性。

另外,如果我们需要恢复某个时间点之前的数据,我们也可以利用binlog日志来实现。首先,我们需要找到对应时间点之前的最近一个全量备份文件,并将其恢复到目标数据库。然后,我们需要找到对应时间点之前的所有增量备份文件(即binlog日志文件),并按照顺序将其应用到目标数据库。这样,我们就可以恢复出目标时间点之前的数据状态了。

  • 使用 Binlog 实时更新/删除 Redis 缓存。利用 Canal,即将负责更新缓存的服务伪装成一个 MySQL 的从节点,从 MySQL 接收 Binlog,解析 Binlog 之后,得到实时的数据变更信息,然后根据变更信息去更新/删除 Redis 缓存;
  • MQ+Canal 策略,将 Canal Server 接收到的 Binlog 数据直接投递到 MQ 进行解耦,使用 MQ 异步消费 Binlog 日志,以此进行数据同步;RocketMQ实现最终一致性;比如基于 RocketMQ 的可靠性消息通信,来实现最终一致性;Canal 组件还可以直接通过 Canal 组件,监控 Mysql 中 binlog 的日志,把更新后的数据同步到 Redis 里面。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当面试MySQLRedis时,常见的问题可以涵盖以下方面: MySQL面试题: 1. 介绍一下MySQL的存储引擎以及各种引擎的特点和适用场景。 2. 什么是索引?MySQL中的索引有哪些类型?它们的区别是什么? 3. 什么是事务?MySQL如何支持事务? 4. 请解释什么是ACID属性,MySQL是如何确保事务的原子性、一致性、隔离性和持久性的? 5. 如何优化MySQL查询性能?可以介绍一些常用的优化方法和技巧。 6. 什么是锁?MySQL中有哪些类型的锁?请解释它们的区别。 7. 如何备份和恢复MySQL数据库? 8. 请介绍一下MySQL的主从复制原理及其应用场景。 Redis面试题: 1. 什么是Redis?它的特点和优势有哪些? 2. Redis支持哪些数据结构?请举例说明它们的应用场景。 3. Redis的数据持久化机制有哪些? 分别是怎么实现的? 4. Redis是否支持多线程?如果不支持,那它是如何实现高并发的? 5. Redis如何实现分布式锁?可以介绍一下相关的方法和算法。 6. Redis如何实现消息队列功能?可以举例说明。 7. Redis的主从复制原理是什么?它有哪些应用场景? 8. Redis的集群模式是如何工作的?可以介绍一下它的架构和部署方式。 以上只是一些常见的MySQLRedis面试题,实际面试中可能会问到更具体的问题。在准备面试时,建议结合自己的实际经验和深入学习相关知识,以便能够更好地回答面试官的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值