Mysql的主从同步/复制的原理分析

2025-06-07 03:50

本文主要是介绍Mysql的主从同步/复制的原理分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教...

为什么要主从同步?

  • 数据容灾、备份。当我们的数据库只使用一台服务时,如果我们的数据库遭到破坏,例如黑客的攻击、人为操作的失误等等情况。这时候我们就可以在一定程度上保障我们数据库的恢复。
  • 缓解 MySQL 主服务的压力。主服务器写数据,从服务器读数据(读写分离php)。

Mysql主从同步架构有哪些?

一主一从/多从架构

  • 适用于读多写少

双主/多主

  • 适用于读写均匀,同时整体的并发量也不算低,至少超出了单库的承载阈值

多主一从

  • 适用于写大于读

级联复制架构

  • 存在两层从库,这实际上属于一主多从架构的升级版,毕竟如果一个主节点存在多个从节点时,多个从节点都会同时去主节点拉取新数据,如果数据量较大,就会导致主节点的I/O负载过高,因此这种级联复制架构要解决的问题,也就是多个从库会对主库造成太大压力的问题。
  • 下面会根据原理介绍为什么级联复制架构更好

主从架构,都会存在致命硬伤,同时也会存在些许问题需要解决:

  • 硬伤:木桶效应,一个主从集群中所有节点的容量,受限于存储容量最低的哪台服务器。
  • 数据一致性问题:由于同步复制数据的过程是基于网络传输完成的,所以存储延迟性。
  • 脑裂问题:从节点会通过心跳机制,发送网络包来判断主机是否存活,网络故障情况下会产生多主。

Mysql主从复制的原理/整体流程

Mysql的主从同步/复制的原理分析

  • master 服务器会将 SQL 记录通过多 dump 线程写入到 binary log android中。
  • 主库会为每个从库创建一个专属的 dump 线程。
  • slave 服务器开启一个 io thread 线程向服务器发送请求,向 master 服务器请求 binary log。master 服务器在接收到请求之后,根据偏移量将新的 binary log 发送给 slave 服务器。
  • slave 服务器收到新的 binary log 之后,写入到自身的 relay log 中,这就是所谓的中继日志。
  • slave 服务器,单独开启一个 sql thread 读取 relay log 之后,写入到自身数据中。

级联复制架构为什么好?

级联复制架构好的原因大家可以从这个图中可以看出,主库会为每个从库建立一个Dump线程,而Dump线程又担负着重要的作用:将监听到的数据通过网络传输发送给从库。可想而知如果从库过多对给主库带来巨大的IO压力,那如果只给一个从库发送数据,并且这个从库又担任其他从库的复制职责,这样就会减轻我们主库的压力,从而提高我们主库的性能。

Mysql的主从同步/复制的原理分析

Mysql主从复制注意点

从库 I/O 线程主要职责是“接收 + 写 relay log

  • 连接到主库并请求binlog内容
  • 接收主库dump线程发送的binlog事件
  • 将接收到的binlog事件写入从库的relay log(中继日志)
  • 更新从库的master info信息(记录已读取的主库binlog位置)
  • 断点续传:如果连接中断,IO线程会按照配置的重试策略定期尝试重新连接

主库 dump 线程负责从 binlog 中读取变更数据,并“源源不断推送”给从库。

  • 等待从库连接并发送binlog位置请求
  • 根据从库请求的binlog文件名和位置(或GTID)定位读取点
  • 以事件(event)为单位读取binlog内容
  • 将事件发送给从库的I/O线程
  • 在没有新事件时进入等待状态(不是持续推送)
  • 即使主库没有数据变化,主库 dump 线程 也会根据 master_heartbeat_period 发送一个 Heartbeat log event 给 从库 IO 线程,作为 binlog 的一部分传输,用来维持连接活跃、防止超时断线。
  • 主库是怎么判断从库有没有数据?
  • 主库不判断!是从库告诉主库它需要从哪个位置开始同步!

MySQL 支持三种 binlog 格式(也就是传输给从库的数据格式)

STATEMENT (不推荐,有风险)

  • 最早期的方式,记录执行的原始 SQL 语句
  • 优点:可读(看得懂)、日志体积小、性能开销小
  • 缺点:有副作用可能不一致(如 UUID、now())

ROW(依旧是逻辑日志)

  • 不记录 SQL,而是记录每一行数据的变更

对比redolog

  • redolog:表空间X,页号Y,偏移量Z处的数据从0x1234改为0x5678
  • redolog记录的是具体的物理存储位置而ROW仅存储表信息以及数据因此依旧是逻辑日志(这里大家不要混淆以为记录数据就是物理日志)
  • 记录信息:表的 database name + table name、表的 列结构、对应数据行的变化
  • 优点:准确,几乎无副作用问题
  • 缺点:日志体积大、不可读(二进制)、性能开销大(行变更多则更慢)

MIXED

  • 自动在 STATEMENT 和 ROW 之间切换,MySQL 自行判断

主从复制数据的模式(重点)

相信大家看有的博客说Mysql主从模式三种或有四种模式的,错大错特错,其实就两种模式,其他两种是基于第二个模式配置出来的,下面给大家具体介绍一下:

Mysql的主从同步/复制的原理分析

异步复制(默认)

  • 客户端发送请求先写入主库并返回客户端,然后再异步同步给从库
  • 优点:返回给客户端快,不会因为同步从库带来阻塞
  • 缺点:主从数据不一致的风险

半同步模式(推荐)

  • 在异步同步的基础上等待从库返回结果再返回给客户端编程
  • 优点:极大的保证了主库和从库的数据一致性
  • 缺点:对客户端的阻塞带来影响

内部细节

  • 如果从库相应时间过长默认10秒,会切换为异步同步模式

同时为了避免网络延迟造成主库长时间收不到从库的ACK,因此在配置半同步式复制时,会有一个rpl_semi_sync_master_timeout参数来控制超时时间,其默认值是10000ms/10s,如若主库在10s内依旧未收到从库的ACK,则会将复制模式切换成异步模式,切成异步模式后,会在后续网络正常后再次切回半同步模式。

  • 对于多个从库节点可以配置有多少个从库返回就给客户端响应

5.7版本后对主从一致性保障策略

AFTER_SYNC(默认,增强半同步复制)【其他博客的第三种模式】

当主库未收到从库的ACK之前,也不会在主库上提交事务,也就是保证了主从节点的数据强一致性,解决了after-commit中存在的问题。

AFTER_COMMIT(传统半同步复制,可能出现数据不一致)

  • 主库在未收到从库的ACK之前,虽然不会给客户端返回写入成功,但本质上在MySQL中会提交事务,也就是主库中的其他事务是可以看见对应数据的,当此时出现宕机时,就会导致旧主上能查询出的数据,在新主(原本的从库)上无法查询出来了。
  • 两者之间的区别就在于:对主从节点的数据严格性不同,一般情况下,无损复制会比传统半同步复制开销更大一些,因为事务迟迟不提交,会导致对应的锁资源不会主动释放,其他需要获取对应锁资源的事务只能阻塞等待,这会造成主库的整体性能出现一定影响。

同步复制(本身不支持,需通过半同步复制设置为等待所有从库【其他博客中第四种模式】

  • 对于同步复制而言,Master主机将事件发送给Slave主机后会触发一个等待,直到所有Slave节点(如果有多个Slave)返回数据复制成功的信息给Master。这种复制方式最安
  • 全,但是同时,效率也是最差的。

Mysql对于从库的一些优化/策略

  • 从库的执行策略

延迟复制

延迟复制通常用于一些特殊场景,它可以支持从库数据的延迟同步,也就是当从库上的I/O线程,将主库的Bin-log日志请求回来后,从节点的SQL线程并不会立刻解析日志执行,而是等待一段时间后再解析日志执行,这个等待的时间可以由开发者来配置,一般建议设为3~6小时之间。

那延迟复制的好处在于什么呢?

可以防止误删操作,如若在主库上不小心误删了大量数据、表、库或其他数据库对象,因为从库并不是立即执行同步过去的记录,因此可以及时通过从节点上的数据回滚数据。除此之外,也能对一些线上Bug进行实时观测,比如一个无法复现的故障问题发生时,如果发现时还在配置的延迟复制时间内,则可以去到从库上观察。

  • 从库的优化手段并行复制(mysql8.0更完善)

GTID复制(为什么要先说GTID,因为并行复制是基于组复javascript制,而组复制是基于GTID)

  • 在传统的主从架构中,当需要发生主从切换时,需要开发/运维人员手动找到Bin-log的POS同步点,然后执行change master to [new-master-pos]命令,将其他从节点指向新主库,但每个从节点可能同步数据的进度都不一致,因此每个从节点都需要去找到它上次的POS点,然后指向新主库,这个工作是不是听起来就比较繁杂?答案是Yes,不过到了MySQL5.6版本后,开启了GTID复制后,则无需手动寻找POS点!
  • GTID(Global Transaction ID)也就是全局事务标识符的意思,它由节点UUID+事务ID两部分组成,MySQL在第一次启动时都会利用UUID随机生成一个server_id,还记得在之前的《MVCC机制》中聊过的事务ID嘛?MySQL会对每一个写事务都分配一个顺序递增的值作为事务ID,而GTID则是由这两玩意儿组成的,格式为server_uuid:trx_id。
  • 当主库的事务有了这个全局事务标识后,再发生主从切换时就无需手动寻点了,仅需要执行change master to master_auto_position = 1这条命令即可,它会自动去新主库上寻找数据的同步点,也就是MySQL自身就具备断点复制的功能。

为什么需要用GTID代替POS同步点呢?

因为同一个主从集群中,所有节点加入集群的时间可能会不同

假设这个集群中每个节点加入的时间都不一致

  • master:日志文件中的同步点POS=1200。
  • slave1:日志文件中的同步点POS=1100。
  • slave2:日志文件中的同步点POS=800。
  • slave3:日志文件中的同步点POS=200。

此时假设master节点宕机或故障了,slave1成为了新主,那么slave2、slave3也应该成为新主slave1的从节点,但此刻问题就来了:原本slave2、slave3的POS同步点是基于master中的日志而言的,但现在主节点变成了slave1,这时slave2、slave3如何去寻找自己在slave1中的POS点呢?显然MySQL无法自己完成该工作,因此需要人工指定同步点才行。

而GTID出现的原因,就是为了解决上述这个问题,但具体怎么解决的呢?

GTID的工作过程

master在更新数据时,会为每一个写事务分配一个全局的GTID,并记录到Bin-log中。

slave节点的I/O线程拉取数据时,会将读到的记录写到relay-log中,并设置gtid_next值。

slave节点的SQL线程执行前,会读取gtid_next值得知接下来该解析哪条日志并执行。

slave节点的SQL线程在执行时,会先比对自身的Bin-log日志中是否有对应的GTID:

  • 有:意味着该GTID对应的事务已经执行过了,slave会自动忽略掉这条记录。
  • 没有:SQL解析该GTID对应的relay-log记录并执行,再将GTID记录到Bin-log。

GTID自动寻找同步点的原理

  • 开启GTID后,从库会基于它来复制主库的数据,此时发生了主从切换,假设这时主从集群中有多个从节点,MySQL首先会选择距离master的GTID最近的从节点作为新主,然后将其他从节点转变为新主的从库,其他从库会根据自身gtid_next值,去新主的日志文件中做对比,然后找到各自的同步点,继续从新主中复制数据。
  • 因为MySQL挑选的是和旧主GTID值,最接近的从节点作为新主,也就意味着作为新主的从节点,绝对会比其他从节点的数据要完善,因此新主中的GTID值也是最大的!同时,每个从节点中都存在一个gtid_next值,记录着自身下一次要同步数据的GTID值,此时剩下的从节点就可以根据该值,直接去新主中寻找到自己的同步点位置,从而避免了之前那种手动介入的尴尬场景出现。
  • 不过由于GTID复制是基于事务来实现的,这也就代表不支持事务的存储引擎无法使用这种机制,在之前的章节中也聊过,MySQL众多存储引擎中,基本上只有InnoDB支持事务,所以GTID机制基本上只对InnoDB引擎生效。
  • 组复制

GTID复制则是组复制的实现基础,而组复制则是并行复制的基础,那么什么叫做组复制呢?组复制是指将一组并行执行的事务,全部放入到一个GTID中记录,后续从节点同步数据时,会一次性读取这一组事务解析并执行,与传统的GTID区别如下:

  • 传统的GTID值由节点ID+事务ID组成:12EEA4RD6-45AC-667B-33DD-CCC55EF718D:88。
  • 组复制的GTID通过逗号分隔:12EEA4RD6-45AC-667B-33DD-CCC55EF718D:89, 12EEA4RD6-45AC-667B-33DD-CCC55EF718D:89-94, ......。

MySQL如何实现事务分组的呢?

  • MySQChina编程L提交事务时内部会调用ordered_commit函数来处理相关工作,其函数执行的逻辑流程图如下:

Mysql的主从同步/复制的原理分析

当一个事务提交时都会调用ordered_commit函数,首先会将事务加入等待事务组,接着会经过三个核心步骤:FLUSH、SYNC、COMMIT,对应的也会有三个队列,它们三者的工作原理都大致相同:

  • ①如果某个事务进入FLUSH队列时,该队列还是空的,则这个事务会担任“队长”的角色。
  • ②当后续其他事务进入队列时,发现队列不为空,则会将提交工作委托给队长来完成。
  • ③如上图中的「事务1」则是队长,后续的都是队员,但队长不会无限制等待队员到来:

从队长加入的时间点开始,当超出binlog_group_commit_sync_delay规定的时间后,就会进行一次组提交。

  • 同一时刻只允许一组事务做这些工作,也就是当有另外一组事务提交时,需要等待上一组事务提交完成。
  • 在做组提交工作时,会将当前事务组的内容记录到Bin-log日志中,同时会将这组事务记录成一个GTID,不同事务之间通过,逗号分隔(实际过程更为复杂,这里只做简单讲解)。

并行复制

在MySQL5.6之前的版本中,从库同步数据时,所有数据同步工作都是基于单线程完成的,也就是不管主库上的数据是不是多线程并发写入的,从库上只会有一条SQL线程来执行解析执行工作。

到了MySQL5.6之后,引入了并行复制的思想,但5.6中的并行复制极其鸡肋,基本无人问津,因为是基于库级别的并行复制,也就是一个从节点对应多个主节点时,有几个主节点就开几条SQL线程去解析并写入数据,即多主一从架构中才会用到。

因为官方最初在实现并行复制时,一直纠结锁冲突的问题,所以为了防止并行执行时出现数据冲突,就造出了上面那种库级别的并行复制,为啥要纠结锁/数据冲突呢?

比如从库I/O线程在主库中请求了100条记录回去,从库中开100条SQL线程解析并执行这些记录,如果其中有两条记录操作的是同一条数据,就会出现锁冲突问题。

  • 但上述原因不是最主要的,最主要的是并发执行的顺序问题,如果主库上对于一条数据是先改后删,从库在并发执行时,因为多线程执行的无序性,把执行顺序改为了先删后改,这显然就会导致数据冲突,因此变更操作很难实现并行复制。
  • 到了MySQL5.7中,才基于组复制技术实现了真正意义上的并行复制,因为能够在同一时间内提交的事务,绝对是不存在锁冲突的,所以可以开启多条线程同时执行一个组中不同的事务,但这个思想是从mariadb中照抄过来的~

一句话来总结就是:主库上是咋样并发写入数据的,从库也会开启对应的线程数去并发写入。

在5.7中官方为这种机制命名为enhanced multi-threaded slave,简称MTS机制,同时为了兼容5.6版本中的并行复制,又多加入了一个slave-parallel-type参数:

  • DATABASE:默认的并行复制模式,表示基于库级别的来完成并行复制。
  • LOGICAL_CLOCK:表示基于组提交的方式来完成并行复制。

并行复制出现的意义是什么?

能够在很大程度上提升从库复制数据的速度,也就是能够让从库的数据实时性提升,尤其是无损复制模式中,主节点需要等待从节点的ACK才会真正提交事务,从库使用并行复制后,能够在一定程度上解决从库的复制延迟问题。

不过虽然5.7中的并行复制,在一定程度上解决了原有的从库延迟问题,但如果一个新的从节点加入集群时,因为要从头开始同步数据,这种并行复制的模式依旧存在效率问题,而到了MySQL8.0中,对于并行复制技术提出了真正的解决之道,也就是基于writeset的MTS技术。

主从数据一致性的解决方案

读写分离数据一致性场景:一个用户将个人信息修改后,然后再次查看时,发现个人信息依旧是修改之前的原数据,这时用户就有可能再次修改,经过反反复复多次修改后,用户发现依旧未生效

想要解决上述这种读写分离导致的数据不一致性,主要有四种解决方案:

业务逻辑做改变、复制方式做更改、数据库架构做调整、引入第三方中间件。

改变业务逻辑

  • 对业务做一定更改,比如当用户立即修改数据后,因为在从库读不到数据,所以先显示一个审核状态,这样能够给出用户的反馈,从而避免用户再次重复操作
  • 这种方式属于和数据不一致妥协的方案,接受一定的数据延迟,不过这种方式并不适用于一些对数据实时性要求较高的场景

更改复制方式

  • 在之前曾聊过MySQL四种数据同步复制的方式,即全同步、异步、半同步与无损复制,MySQL默认会是异步复制模式,即主节点写入数据后会立即返回成功的状态给客户端,这样能够确保性能达到最佳,但如果对实时性要求较高,可以将其改为全同步或半同步模式。

调整数据库架构

  • 如果无法接受主从架构带来的短期数据不一致,那可以升级服务器硬件,并将架构恢复成单库架构,所有读写操作都走单库完成,这就自然不会出现数据不一致问题。
  • 但如果升级硬件配置后,无法承载客户端的访问压力时,可将整体架构升级到分库分表架构,制定好合适的分片策略和路由键,每次读写数据都根据业务不同,操作不同的库。

引入第三方中间件

  • 前面聊到的三种方案,多多少少都存在一些局限性,因为要么接受数据不一致、要么损失性能、要么使用更高规模的架构处理,但这些方案似乎都存在令人不能接受的后患,那有没有一种万全之策来解决这个问题呢?答案是有的,就是引入Canal中间件来监控主节点的Bin-log日志。
  • 在之前讲主从同步数据原理时,曾讲到过,主节点上存在一个log dump线程会监听Bin-log日志,当日志出现变更时会通知从节点来拉取数据,而Canal的思想也是一样的,会监控主节点的Bin-log日志,当发生变更时,就直接去拉取数据,然后直接推送给从节点写入。
  • 但这种方式也无法做到真正的数据实时性,毕竟Canal监听变更、拉取数据、推送数据都需要时间,这部分的时间开销必然存在,只是它会比从库去主库上拉取,速度会更快一些罢了。
  • 一般企业内部都会引入Canal来解决数据不一致问题,因为它不仅仅只能解决主从延迟问题,还能解决MySQL-ES、MySQL-Redis.....等多种数据不一致的场景
  • 也可以制定某些敏感数据走主库查询,这样能够确保数据的实时性,但容易模糊读写分离的界限,不过因为不需要引入额外的技术,所以在某些情况下也是个不错的方案。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于Mysql的主从同步/复制的原理分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154947

相关文章

Nacos注册中心和配置中心的底层原理全面解读

《Nacos注册中心和配置中心的底层原理全面解读》:本文主要介绍Nacos注册中心和配置中心的底层原理的全面解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录临时实例和永久实例为什么 Nacos 要将服务实例分为临时实例和永久实例?1.x 版本和2.x版本的区别

Python实现自动化Word文档样式复制与内容生成

《Python实现自动化Word文档样式复制与内容生成》在办公自动化领域,高效处理Word文档的样式和内容复制是一个常见需求,本文将展示如何利用Python的python-docx库实现... 目录一、为什么需要自动化 Word 文档处理二、核心功能实现:样式与表格的深度复制1. 表格复制(含样式与内容)2

如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socket read timed out的问题

《如何解决Druid线程池Cause:java.sql.SQLRecoverableException:IO错误:Socketreadtimedout的问题》:本文主要介绍解决Druid线程... 目录异常信息触发场景找到版本发布更新的说明从版本更新信息可以看到该默认逻辑已经去除总结异常信息触发场景复

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

MySQL 表的内外连接案例详解

《MySQL表的内外连接案例详解》本文给大家介绍MySQL表的内外连接,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录表的内外连接(重点)内连接外连接表的内外连接(重点)内连接内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我

MySQL的ALTER TABLE命令的使用解读

《MySQL的ALTERTABLE命令的使用解读》:本文主要介绍MySQL的ALTERTABLE命令的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、查看所建表的编China编程码格式2、修改表的编码格式3、修改列队数据类型4、添加列5、修改列的位置5.1、把列

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

解决mysql插入数据锁等待超时报错:Lock wait timeout exceeded;try restarting transaction

《解决mysql插入数据锁等待超时报错:Lockwaittimeoutexceeded;tryrestartingtransaction》:本文主要介绍解决mysql插入数据锁等待超时报... 目录报错信息解决办法1、数据库中执行如下sql2、再到 INNODB_TRX 事务表中查看总结报错信息Lock

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性