写在前面:
对于线上运行主从复制架构的环境而言,相信有不少人和笔者同样,都或多或少的遇到过主从延迟的问题。以前笔者写过一篇文章 主从复制延迟缘由剖析 来说解主从复制延迟的缘由,可光是知道缘由还不行,怎么解决这个主从延迟的问题才是重头戏!笔者,带着这个疑问,在网上也是查阅了诸多资料,而后去其糟粕,根据本身的理解和查阅的资料整理成了本文 MySQL并行复制探索!。事先申明,本文内容是笔者本身的理解,不表明权威性,仅供各位同行作个参考,本身呢,也作个记录。本着实事求是的原则,对于网上的诸多资料,笔者本身也只是信5分,怀疑5分,就算是官方的资料,有的也有bug不是,只有本身动手实践过了,才能相信,才有发言权,其余的,一切都是虚的!html
并行复制产生的背景:
由于I/O thread和SQL thread是单线程工做的,而Master是能够多线程写入的,因此主从不免形成延迟
基于此,在5.6,5.7,8.0版本都在SQL线程上实现了多线程,来提高slave的并发度mysql
为何不是I/O多线程?
I/O不必多线程,由于I/O线程并非瓶颈啊 (没怎么理解)
sql
并行复制的目的:
让Slave SQL线程尽量以多线程工做,解决复制延迟的问题
并行复制实现的前提:
可以进行并行复制,关键在于多事务之间是否有锁冲突数据库
MySQL5.6基于schema的并行复制:
应用场景:
比较适用于一个库中有多个schema的场景服务器
并行复制的原理:
MySQL5.6开启并行复制功能后,SQL线程变成coordinator线程,由其判断是否能够并发执行,这意味着一个worker线程能够处理一个数据库的连续事务,而不用等待其它数据库完成
若是能够并行执行,选择worker线程执行二进制日志
若是不能够并行执行,如:DDL或者跨schema操做,则等待全部的worker线程执行完成以后再执行当前日志多线程
摘自网上的一张图片,供参考理解:架构
基于schema的并行复制带来的问题:
由于MySQL5.6并行复制只是基于schema,但对于单库多表的复制场景是没法实现的,甚至性能可能还达不到原来的单线程复制,而在实际工做中单库多表比多库多表的场景更为常见。
MySQL5.6基于schema级别的并发复制能够解决业务表放在不一样的DATABASE下同步延迟的问题,可是在实际生产中大部分表仍是放在同一个库中的,这种状况即便设置slave_parallel_workers大于0,也没法进行并发。在高并发的状况下,依然会形成主从复制延迟
并发
MySQL5.6并行复制开启:(前提是配置好主从复制环境)app
mysql> stop slave;ide
Query OK, 0 rows affected (0.03 sec)
mysql> set global slave_parallel_workers=8;
Query OK, 0 rows affected (0.05 sec)
mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.07 sec)
参考文档:
http://blog.itpub.net/23718752/viewspace-2135701/
https://www.cnblogs.com/elontian/p/9989007.html
MySQL5.6并行复制原理图:
并行复制参数说明:
slave_parallel_workers:
Number of applier threads for executing replication transactions in parallel. A value of 0 disables slave multithreading. Not supported by MySQL Cluster
官方文档:
https://dev.mysql.com/doc/refman/5.6/en/replication-options-reference.html
MySQL5.7并行复制原理:
基于组复制(group commit)实现
如何知道事务是否在同一组中?
在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。那么若是用户没有开启GTID功能,即:将参数gtid_mode设置为OFF呢?
MySQL 5.7引入了称之为Anonymous_Gtid(ANONYMOUS_GTID_LOG_EVENT)的二进制日志event类型,组提交信息存放在 Anonymous_Gtid 中。
当开启GTID时,每个操做语句(DML/DDL)执行前就会添加一个GTID事件,记录当前全局事务ID;同时在MySQL 5.7版本中,组提交信息也存放在GTID事件中,有两个关键字段last_committed,sequence_number就是用来标识组提交信息的。在InnoDB中有一个全局计数器(global counter),在每一次存储引擎提交以前,计数器值就会增长。在事务进入prepare阶段以前,全局计数器的当前值会被储存在事务中,这个值称为此事务的commit-parent(也就是last_committed)。last_committed表示事务提交的时候,上次事务提交的编号,若是事务具备相同的last_committed,表示这些事务都在一组内,能够进行并行的回放。而sequence_number是顺序增加的,每一个事务对应一个序列号。
这意味着在MySQL 5.7版本中即便不开启GTID,每一个事务开始前也是会存在一个Anonymous_Gtid,而这个Anonymous_Gtid事件中就存在着组提交的信息。反之,若是开启了GTID后,就不会存在这个Anonymous_Gtid了,从而组提交信息就记录在非匿名GTID事件中。
MySQL如何将这些事务进行分组的?
一个组提交的事务都是能够并行回放,由于这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(不然就不可能提交)
MySQL5.7并行复制简介:
1)MySQL 5.7才可称为真正的并行复制,这其中最为主要的缘由就是slave服务器的回放与master是一致的,即master服务器上是怎么并行执行的,那么slave上就怎样进行并行回放。再也不有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)
2)MySQL5.7并行复制支持表级
3)Enhanced Multi-threaded Slaves(MTS)
MySQL5.7并行复制参数:
SHOW VARIABLES LIKE 'slave_parallel_%'
Variable_name Value
slave_parallel_type DATABASE (变量slave-parallel-type能够有两个值:DATABASE 默认值,基于库的并行复制方式;LOGICAL_CLOCK:基于组提交的并行复制方式(基于表))
slave_parallel_workers 0
MySQL 5.7并行复制配置与调优:
# slave
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
slave_preserve_commit_order=1
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
参考文档:
http://www.javashuo.com/article/p-xgiumuqc-k.html
https://www.imooc.com/learn/589
https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html
http://www.ywnds.com/?p=3894
MySQL5.7在线开启并行复制(多线程复制):
参考视频:https://www.imooc.com/video/10921
在Slave服务器上中止全部链路的复制
stop slave
set global slave-parallel-type=LOGICAL_CLOCK
set global slave-parallel-workers=16
start slave
show processlist(看到16个SQL线程)
MySQL5.7应用事务顺序和realy log记录事务顺序不同的问题:MySQL 5.7后的MTS能够实现更小粒度的并行复制,但须要将slave_parallel_type设置为LOGICAL_CLOCK,但仅仅设置为LOGICAL_CLOCK也会存在问题,由于此时在slave上应用事务的顺序是无序的,和relay log中记录的事务顺序不同,这样数据一致性是没法保证的,为了保证事务是按照relay log中记录的顺序来回放,就须要开启参数slave_preserve_commit_order