Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > Mysql的主从同步/复制的原理

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

作者:奇怪的爪哇岛开发

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

为什么要主从同步?

Mysql主从同步架构有哪些?

一主一从/多从架构

双主/多主

多主一从

级联复制架构

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

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

级联复制架构为什么好?

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

Mysql主从复制注意点

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

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

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

STATEMENT (不推荐,有风险)

ROW(依旧是逻辑日志)

对比redolog

MIXED

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

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

异步复制(默认)

半同步模式(推荐)

内部细节

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

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

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

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

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

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

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

延迟复制

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

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

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

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

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

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

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

此时假设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自动寻找同步点的原理

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

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

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

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

并行复制

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

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

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

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

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

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

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

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

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

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

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

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

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

改变业务逻辑

更改复制方式

调整数据库架构

引入第三方中间件

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文