为保证数据库的高可用性,可以采用冗余的方式,但是数据冗余带来的问题是数据一致性问题。主从模式就是该方式的一种集群实现架构,其主要优势就是简单灵活,能满足多种需求。是一种比较主流的用法,同时其劣势也很明显:写操作高可用需自行处理。
MySQL主从模式是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,从节点可以复制主数据库中的所有数据库,或者特定的数据库,或者特定的表。

主从复制模式离不开Binlog(存储引擎InnoDB_Redo Log和BinLog) 。

  由上图示可知主从复制整体分为以下三个步骤:
    1)主库将数据库的变更操作记录到Binlog日志文件中;
    2)从库读取主库中的Binlog日志文件信息写入到从库的Relay Log中继日志中;
    3)从库读取中继日志信息在从库中进行Replay,更新从库数据信息。

  在上述三个过程中,涉及了Master的BinlogDump Thread和Slave的I/O Thread、SQL Thread,它们的作用如下:

    1)Master服务器对数据库更改操作记录在Binlog中,BinlogDump Thread接到写入请求后,读取Binlog信息推送给Slave的I/O Thread。

    2)Slave的I/O Thread将读取到的Binlog信息写入到本地Relay Log中。

    3)Slave的SQL Thread检测到Relay Log的变更请求,解析relay log中内容在从库上执行。

  如下是异步复制时序图:

  上述过程都是异步操作,俗称异步复制;既然是异步就必然存在数据延迟现象,如果宕机那必然导致数据丢失。

  存在问题就得解决,怎么解决呢?使用:1)半同步复制—解决数据丢失的问题;2)并行复制—-解决从库复制延迟的问题。

  半同步复制:

    为了提升数据安全,MySQL让Master在某一个时间点等待Slave节点的 ACK(Acknowledgecharacter)消息,接收到ACK消息后才进行事务提交,这也是半同步复制的基础。介绍半同步复制之前先快速过一下 MySQL 事务写入碰到主从复制时的完整过程,主库事务写入分为 4个步骤:

      1)InnoDB Redo File Write (Prepare Write)

      2)Binlog File Flush & Sync to Binlog File

      3)InnoDB Redo File Commit(Commit Write)

      4)Send Binlog to Slave

    当Master不需要关注Slave是否接受到Binlog Event时,即为传统的主从复制。

    当Master需要在第三步等待Slave返回ACK时,即为 after-commit,半同步复制。

    当Master需要在第二步等待 Slave 返回 ACK 时,即为 after-sync,增强半同步。

  下图是 MySQL 官方对于半同步复制的时序图,主库等待从库写入 relay log 并返回 ACK 后才进行Engine Commit。

  其流程总结如下:

    1)Master服务器对数据库更改操作记录在Binlog中,BinlogDump Thread接到写入请求后,读取Binlog信息推送给Slave的I/O Thread。

    2)Slave的I/O Thread将读取到的Binlog信息写入到本地Relay Log中。

    3):

      3-1)当Master需要在Binlog File Flush & Sync to Binlog File时等待 Slave 返回 ACK 时,即为 after-sync,增强半同步。

      3-2)当Master需要在InnoDB Redo File Commit(Commit Write)时等待 Slave 返回 ACK 时,即为 after-commit,半同步复制。

      即SlaveACK的时机直接影响Master Commit。

    4)Slave的SQL Thread检测到Relay Log的变更请求,解析relay log中内容在从库上执行。

  并行复制:

    MySQL的主从复制延迟一直是受开发者最为关注的问题之一,MySQL追加并行复制功能,目的就是为了改善复制延迟问题,并行复制称为enhanced multi-threaded slave(简称MTS)。在从库中有两个线程IO Thread和SQL Thread,都是单线程模式工作,因此有了延迟问题,我们可以采用多线程机制来加强,减少从库复制延迟。(IO Thread多线程意义不大,主要指的是SQL Thread多线程)。

    MySQL 5.7实现了基于组提交的并行复制,该种方式才可称为真正的并行复制,这其中最为主要的原因就是slave服务器的回放与master服务器是一致的,即master服务器上是怎么并行执行的slave上就怎样进行并行回放。不再有库的并行复制限制。

    MySQL 通过对事务进行分组(Group Commit技术将事务的提交阶段分成了Flush、Sync、Commit三个阶段,每个阶段维护一个队列,并且由该队列中第一个线程负责执行该步骤,这样实际上就达到了一次可以将一批事务的Binlog fsync到磁盘的目的,这样的一批同时提交的事务称为同一个Group的事务),当事务提交时,它们将在单个操作中写入到二进制日志中。如果一批事务是同时Commit的,那么这些事务必然不会有互斥的持有锁,也不会有执行上的相互依赖,因此这些事务必然可以并行的回放——即可以在Slave上并行执行,所以通过在主库上的二进制日志中添加组提交信息。但是该并行复制基于一个前提,即所有已经处于prepare阶段的事务,都是可以并行提交的。这些当然也可以在从库中并行提交,因为处理这个阶段的事务都是没有冲突的。在一个组里提交的事务,一定不会修改同一行。这是一种新的并行复制思路,完全摆脱了原来一直致力于为了防止冲突而做的分发算法,等待策略等复杂的而又效率底下的工作。

    InnoDB事务提交采用的是两阶段提交模式。一个阶段是prepare,另一个是commit。5.7引入了新的变量slave-parallel-type,其可以配置的值有:DATABASE(默认值,基于库的并行复制方式)、LOGICAL_CLOCK(基于组提交的并行复制方式)。

    那么如何知道事务是否在同一组中,生成的Binlog内容如何告诉Slave哪些事务是可以并行复制的?

    在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。为了避免用户没有开启GTID功能(gtid_mode=OFF),MySQL 5.7又引入了称之为Anonymous_Gtid的二进制日志event类型ANONYMOUS_GTID_LOG_EVENT。

    last_committed表示事务提交的时候,上次事务提交的编号,如果事务具有相同的last_committed,表示这些事务都在一组内,可以进行并行的回放。

    MySQL8.0 是基于write-set的并行复制——引入了一种新的机制来判断事务能否并行回放,通过检测事务在运行过程中是否存在写冲突来决定从机上的回放顺序,这使得从机上的并发程度不再依赖于主机。MySQL会有一个集合变量来存储事务修改的记录信息(主键哈希值),所有已经提交的事务所修改的主键值经过hash后都会与那个变量的集合进行对比,来判断改行是否与其冲突,并以此来确定依赖关系,没有冲突即可并行。这样的粒度,就到了 row级别了,此时并行的粒度更加精细,并行的速度会更快。

  MySQL支持的复制类型有以下几种:

    1、基于语句的复制(逻辑复制):在主服务器上执行的SQL语句,在从服务器上执行同样的语句。默认采用基于语句的复制,效率比较高。一旦发现无法精确复制就会自动选择基于行的复制。

    2、基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍。

    3、混合类型的复制:默认采用基于语句的复制,一旦发现基于语句的列无法精确复制时,就会采用基于行的复制。

  后续将进行上述三种复制方式的实践,分别是MySQL集群架构之主从模式异步复制实践 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com),MySQL集群架构之主从模式半同步复制实践 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com),MySQL集群架构之主从模式并行复制实践 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)。

作者:Jeebiz  创建时间:2023-01-18 12:01
最后编辑:Jeebiz  更新时间:2024-08-22 10:22