1 MySQL主从复制方式说明
主从复制主要有如下3个步骤:
- master 节点上的binlogdump 线程,在slave 与其正常连接的情况下,将binlog 发送到slave 上。
- slave 节点的I/O Thread ,通过读取master 节点binlog 日志名称以及偏移量信息将其拷贝到本地relay log 日志文件。
- slave 节点的SQL Thread,该线程读取relay log 日志信息,将在master 节点上提交的事务在本地回放,达到与主库数据保持一致的目的。
MySQL5.5 及以前的复制有三个线程且都是单线程:
Binlog Dump(主) ‐‐> IO Thread(从) ‐‐> SQL Thread(从)。
Master 这边是通过并发线程提交,事务通过LSN 写入binlog;但是Slave 只有一个IO 线程和SQL 线程,是单线程,所以在业务大的情况下就很容易造成主从延时。 所以从5.6 开始引入了多线程复制(MTS)。
版本 | MTS机制 | 实现原理 |
---|---|---|
5.6 | Database | 基于库级的并行复制 |
5.7 | LOGICAL_CLOCK / COMMIT_ORDER | 基于组提交的并行复制 |
5.7.22 | WRITESET / WRITESET_SESSION | 基于WRITESET的并行复制 |
查看多线程复制类型和复制线程数
mysql> SELECT @@slave_parallel_type,@@slave_parallel_workers;
+-----------------------+--------------------------+
| @@slave_parallel_type | @@slave_parallel_workers |
+-----------------------+--------------------------+
| LOGICAL_CLOCK | 16 |
+-----------------------+--------------------------+
1 row in set (0.00 sec)
2 基于DATABASE的多线程复制
2.1 说明
在MySQL 5.6中引入该特性,如果主库上存在多个数据库,每个数据库的事务相互独立于其他数据库,因此只需要保证数据库内部的事务运行顺序和主库上的运行顺序一致,就可以保证主库和从库上的数据相同。
在MYSQL中开启并行复制功能,SQL线程会变成coordinator线程,coordinator线程会对二进制日志的event进行判断:
1、如果判断事件可以被并行执行,那么选择相应worker线程应用BINLOG事件
2、如果判断事件不可以被并行执行,如DDL操作或跨schema事务,则等待所有worker线程执行完成后,再执行该BINLOG事件。
coordinator线程不仅分发BINLOG事件,也可以执行BINLOG事件。
当实例上数据库数量较少或应用主要对某个数据库进行读写,并行复制的性能可能会比单线程复制更差。
对于跨数据库的事务或跨数据库的外键,都会导致无法多线程并行执行。
基于DATABASE的多线程复制模式下执行位点问题:
- MySQL使用Low-Water-Mark标记来最小已完成事件点,当发生宕机恢复时,根据Low-Water-Mark标记值来重放其后面的事件,而其中部分事件可能已被执行,重复执行可能会导致SQL线程异常或数据异常。
- MySQL使用checkpint方式来推进APPLY主库BINLOG的位置,使用SHOW SLAVE STATUS命令显示的Exec_master_log_pos值是最近一次checkpint时的位点,而不是最后一个APPLY事务的值。
- 运行SQL_SLAVE_SKIP_COUNTER命令存在风险,可能会跳过其他事务。
- 对从库进行备份获取到的执行位点可能不是正确位点
- 当多个数据库执行进度相差较大时,可以使用START SLAVE UNITL SQL_AFTER_MTS_GAPS语句来等待延迟较大的数据库执行。
Database级别的并行复制效果并不特别好,因为大多数生产的架构依然习惯于单库多表的架构,这种情况下MTS依然还是单线程的效果。但Database级别并行复制的好处是可以兼容任何二进制日志,从机都可以进行库级别的并行回放。
2.2 参数配置
主库
slave_parallel_type = DATABASE
从库
slave_parallel_type = DATABASE
slave_parallel_workers = N
3 基于LOGICAL_CLOCK(COMMIT_ORDER)的多线程复制
3.1 说明
在MySQL 5.7版本中引入,在主库上的某个时间点上,所有完成excution处于prepare阶段的事务都处于一个”相同的数据库版本”上,这些事务之间不存在阻塞或者依赖,因此可以赋予一个相同的时间戳;拥有相同时间戳的事务可以在从库上并行执行并且不会导致相互等待。如果事务间存在依赖,那么被阻塞的事务肯定处于Execution状态而不会进入Prepare状态。
Commit_Order的并行复制是在主数据库实例事务提交时,写入一些额外信息,从而在从机回放时,可以根据这些信息判断是否可以进行并行的回放。这种实现机制的巧妙之处在于:同一组提交的事务之间是不冲突的,因此可以并行回放。
COMMIT_ORDER的并行复制机制虽好,然而需要有一个条件:每组提交事务要足够多。即,业务量要足够大。但是当你的业务量比较小,并发度不够时,基于COMMIT_ORDER的并行复制依然会退化为单线程复制。
多线程复制模式下的事务执行顺序:
MySQL通过参数slave_preserve_commit_order可以控制Slave上的binlog提交顺序和Master上的binlog的提交顺序一样,保证GTID的顺序。该参数只能用于开启了logical clock并且启用了binlog的复制。即对于多线程复制,该参数用来保障事务在slave上执行的顺序与relay log中的顺序严格一致。开启该参数可能会有一点的消耗,因为会让slave的binlog提交产生等待。
3.2 参数配置
主库
slave_parallel_type = LOGICAL_CLOCK
从库
slave_preserve_commit_order = 1
slave_parallel_type = LOGICAL_CLOCK
slave_parallel_workers = 16
4 基于WRITESET的多线程复制
4.1 说明
MySQL 5.7.22 中推出了基于WriteSet机制的并行复制,从机并行执行无需依赖组复制机制。简单来说,WriteSet并行复制的思想是:不同事务的不同记录不重叠,则都可在从机上并行回放,可以看到并行的力度从组提交细化为记录级。WriteSet的性能比Commit_Order要快5~6倍。
4.2 参数配置
主库
slave_parallel_type = LOGICAL_CLOCK
transaction_write_set_extraction = XXHASH64
binlog_transaction_dependency_tracking = WRITESET
binlog_transaction_dependency_history_size = 25000
从库
slave_preserve_commit_order = 1
slave_parallel_type = LOGICAL_CLOCK
slave_parallel_workers = 16