---
title: 并行复制
description: "在主从复制过程中，从库的IO线程用于接收binlog并写入relay log，从库SQL线程用于回放relay log把更新写入本地磁盘。默认情况下，每个从库只有一个SQL线程，客户端在高并发情况下，若主库产生binlog的速度超过从库消费binlog的速度，会导致主库和从库之间存在数据差距。并行复制通过增加从库SQL线程的并行度来降低主库和从库之间的数据差距。"
url: https://www.hikunpeng.com/document/detail/zh/kunpengdbs/ecosystemEnable/MySQL/kunpengmysqlmasterslave_03_0007.html
sourcePath: /source/zh/kunpengdbs/ecosystemEnable/MySQL/kunpengmysqlmasterslave_03_0007.html
indexId: c61d8198caf05ee674ff44fadd8bda646c85aee6f1eb390f19568a661763a0be80
---
# 并行复制

#### 简介

在主从复制过程中，从库的IO线程用于接收binlog并写入relay log，从库SQL线程用于回放relay log把更新写入本地磁盘。默认情况下，每个从库只有一个SQL线程，客户端在高并发情况下，若主库产生binlog的速度超过从库消费binlog的速度，会导致主库和从库之间存在数据差距。并行复制通过增加从库SQL线程的并行度来降低主库和从库之间的数据差距。


#### 业务流程

MySQL 5.7引入基于Logic clock的并行复制方案。Logic clock并行复制有Commit-Parent-Based和Lock-Based两种模式。而在MySQL 5.7.21版本之后，增加了WriteSet复制。

在介绍这两种模式之前，先介绍一下MySQL事务提交方式。

1. MySQL事务提交。
  MySQL事务提交主要分为两个阶段，准备阶段和提交阶段。

  准备阶段：SQL执行成功以后，生成xid信息、redo和undo的内存日志，调用prepare方法将事务状态设为TRX_PREPARED，并将redo log写入磁盘。

  提交阶段：事务涉及的所有存储引擎的prepare都已执行成功，调用TC_LOG_BINLOG::log_xid方法将SQL语句写到binlog日志，然后调用存储引擎的commit完成事务的提交，将事务改为TRX_NOT_STARTED状态。

2. MySQL组提交。
MySQL 5.6版本引入了组提交（Group Commit），并将提交过程分为Flush stage、Sync stage和Commit stage三个阶段。

  - Flush stage（缓存阶段）：将所有已注册线程写入binlog缓存。
  - Sync stage（同步阶段）：将binlog缓存同步到磁盘。
  - Commit stage（提交阶段）：leader线程根据顺序调用存储引擎提交事务。
每个阶段都有各自的队列，当一个线程注册到一个空队列，该线程就作为该队列的leader，后注册到该队列的线程均为follower，leader控制队列中follower执行操作。leader线程带领当前队列的所有follower线程到下一个阶段去执行。
在Commit stage，实际做的是存储引擎提交，参数binlog_order_commits会影响提交行为。如果设置On，存储引擎的提交变为串行操作，以队列的顺序为提交顺序。如果设置成OFF，组内的每个事务会在leader线程完成commit之后，各自进行存储引擎层的提交操作。
组提交（Group Commit）是Logic clock并行复制的基础。Group Commit将所有的事务进行了分组，并为每个事务分配了last_committed和sequence_number。last_committed表示数据库中上一个事务的提交编号，同一组事务的last_committed的值相同；sequence_number是顺序增长的，每个事务对应一个序列号。


1. Commit-Parent-Based和Lock-Based两种模式。
图1 并行复制的流程

图1中C代表last_committed，表示事务进入到prepare阶段前获取到的最大commit逻辑时间。
S代表sequence_number，表示每个事务commit结束后的逻辑时间。

  - Commit-Parent-Based模型：

    a. 拥有相等C的事务属于同一组。同一组事务没有冲突，可以并行。
    b. Trx1、Trx2、Trx3、Trx4不属于同一个事务组，无法并行。
    c. Trx5、Trx6、Trx7属于同一个事务组，它们的C是4，可以并行。
    d. Trx8、Trx9属于同一个事务，它们的C是6，可以并行。但必须等待Trx7结束以后才能执行。
Commit-Parent-Based模式下，如果master的组事务越少，并行度越低。
  - Lock-Based模式：
出现重叠的事务（事务的C和S之间存在交集），说明这些事务在prepare阶段可以一起执行，意味着没有锁冲突，可以并行。

    a. Trx1、Trx2、Trx3、Trx4没有重叠，不可以并行。
    b. Trx5、Trx6、Trx7重叠，可以并行。
    c. Trx7、Trx8、Trx9重叠，但它们并不是同一组，可以并行，当Trx5、Trx6执行结束后，Trx7、Trx8、Trx9就可以并行，Lock-Based的并行粒度比Commit-Parent-Based更细。
并行复制的处理流程主要是通过binlog_prepare函数为每个事务生成一个last_committed，进入Flush队列的第一个事务的last_committed作为这个队列的last_committed，调用binlog_cache_data::flush函数可获取每个事务sequence_number，调用Mts_submode_logical_clock:Mts_submode函数实现从库的并行复制。
2. WriteSet复制。
WriteSet通过检测不同事务之间是否存在写冲突，在写入relay log时，会将没有冲突的事务的last_committed值设置为相同的值，重新规划事务的并行回放，所以使用WriteSet并行复制，从库上的并发程度不再依赖于主库，而是基于事务本身的更新冲突来确定并行关系。WriteSet会将主库上已提交事务的被修改的主键和非空的唯一性索引值进行hash运算，并将运算后的值和内存中hash map（hash map存放了最近一段时间内提交的记录）里的值进行对比，判断当前事务与已经提交的事务是否存在冲突，如果不存在冲突，当前事务和已经提交的事务共享相同的last_committed值，若存在冲突，在hash map已提交的WriteSet中删除该冲突事务之前提交的所有WriteSet，并退化到commit_order方式计算last_committed。
图2 WriteSet冲突检测的流程

  a. 在hash map中查找Write Set中row1的hash value，如果找到row1的hash value，说明与hash map存在冲突，将历史map中这行数据的sequence number改为130，同时设置当前提交事务的last_committed为120。
  b. 在hash map中查找Write Set中row7的hash value，如果找到row7的hash value，说明与hash map存在冲突，将hash map中这行数据的sequence number改为130，由于hash map中的sequence number为114，小于120，WriteSet中last_committed不做更改。
  c. 在hash map中查找Write Set中row6的hash value，如果找到行row6的hash value，说明与hash map存在冲突，将hash map中这行数据的sequence number改为130，由于hash map中的seq number为105，小于120，WriteSet中last_committed不做更改。
  d. 在hash map中查找Write Set中row10的hash value，需要将这一行插入到hash map中。
    本次事务row1、row7、row6都检测到冲突，无法在从库中并行执行，而row10没有检测到冲突，可以在从库中执行。


#### 适用场景

并行复制的优点在于提高新增数据在从库上重放的性能，帮助用户更快地查到最新的数据，但并行复制不是实时复制，不保证查到实时更新的数据。
