5.7新特性之多线程复制
文章目录
5.7新特性之多线程复制
一、背景
1.1传统主从复制存在的问题
众所周知,MySQL的从库可以做业务的线性扩展、实现读写分离、数据备份等功能。但是当主库压力比较大的时候,就会产生一个让人头疼的问题,那就是主从复制延迟。主从复制延迟会导致从库数据落后于主库,产生诸多问题。
1.2降低复制延迟的方法
·增大从库innodb_buffer_pool_size的值,使从库可以缓存更多数据,降低IO压力。
·增大innodb_log_file_size、innodb_log_files_in_group的值,降低刷盘IO,提升写入性能。
·将innodb_flush_method设置为O_DIRECT,提升写入性能。
·如果没有特殊需求,关闭从库binlog。
·将参数master_info_repository和relay_log_info_repository设置为TABLE,降低IO压力。
上文这些方法虽然可以降低复制延迟,但是这始终无法真正解决复制延迟问题。
二、MySQL5.6的多线程复制
除了上文的优化方法,其实MySQL还有一种自带的手段,就是开启MySQL的多线程复制。
2.1MySQL5.6多线程复制的实现
对于某一个库来说,它会被绑定到第一个执行它的线程上,这里的绑定不是说以后该数据库的事件都会由该线程执行,还受制于另一个条件:coordinator线程分配事件时以事务为单位,一个事务会分配给该事务中第一个库所绑定worker线程,不会被拆分。如果遇到一个新的库,不能按照上面的规则决定执行的数据库的(即没有绑定线程,而且是该事务中第一个库)则会寻找绑定库最少的worker线程来执行它。
涉及多库操作的语句,在分配这个语句时,coordinator线程会等待这些库的绑定线程都执行完毕,然后再分配这个语句。而如何涉及到的库太多(大于254)或者是一个ddl语句,则会触发一次同步操作,即等待所有线程执行完毕,然后将它分配给0号worker线程。
2.2MySQL5.6的多线程复制的缺点
MySQL5.6的多线程复制要求数据库数量比较多,并且各个库的数据要分布均匀。换句话说,MySQL5.6的多线程复制是基于库级别的,如果binlog row event操作的是不同的schema的对象,在没有DDL的情况下,就可以实现多线程复制。
但是在实际业务场景中,一库多表很常见,多库少表却很少见。这个原因导致MySQL5.6的多线程复制并不适合在实际环境应用,但是不得不承认这的确一个解决复制延迟问题很好的手段。
三、MySQL5.7的多线程复制
MySQL5.6的多线程复制在实际应用起到的作用却很小。直到MySQL5.7版本,MySQL才“真正”支持多线程复制功能,官方称为为enhanced multi-threaded slave(简称MTS)。
3.1MySQL的组提交
在介绍MySQL5.7的多线程复制前,我要先介绍一下组提交。MySQL5.7相对于MySQL5.6在GTID中增加了两个事件(last_committed和sequence_number),根据这两个事件就可以明白什么是组提交:
#190528 9:50:18 server id 330601 end_log_pos 971 CRC32 0x0d6b7893 GTID last_committed=2 sequence_number=3 rbr_only=yes
#190528 9:50:23 server id 330601 end_log_pos 1288 CRC32 0x02f70237 GTID last_committed=3 sequence_number=4 rbr_only=yes
#190528 9:50:24 server id 330601 end_log_pos 1605 CRC32 0xd613b4bf GTID last_committed=4 sequence_number=5 rbr_only=yes
可以看到以往binlog中下一个事务的last_committed永远都和上一个事务的sequence_number是相等的。
#190629 21:21:07 server id 330601 end_log_pos 1420595 CRC32 0xee9fca87 GTID last_committed=3460 sequence_number=3506 rbr_only=yes
#190629 21:21:07 server id 330601 end_log_pos 1420998 CRC32 0x64a67854 GTID last_committed=3460 sequence_number=3507 rbr_only=yes
#190629 21:21:07 server id 330601 end_log_pos 1421401 CRC32 0x89930386 GTID last_committed=3460 sequence_number=3508 rbr_only=yes
在MySQL5.7开启组提交的binlog中各事务的last_committed是相同的,这意味着多个事务是作为一个组提交的,这些事务在perpare阶段获取相同的last_committed而且相互不影响,最终是会作为一个组进行提交。这就是所谓的组提交。
MySQL5.7的组提交通过参数group_commit设置。
mysql> show variables like '%group_commit%';
+-----------------------------------------+-------+
| Variable_name | Value |
+-----------------------------------------+-------+
| binlog_group_commit_sync_delay | 0 |
| binlog_group_commit_sync_no_delay_count | 0 |
+-----------------------------------------+-------+
2 rows in set (0.00 sec)
binlog_group_commit_sync_delay控制着日志在刷盘前日志提交要等待的时间,0代表提交后立即刷盘,当设置为N(大于0)的时候,就允许多个事务的日志同时间一起提交刷盘,也就是我们说的组提交。组提交是多线程复制的基础。
binlog_group_commit_sync_no_delay_count ,这个参数优先于binlog_group_commit_sync_delay,在等待时间内,如果事务数达到binlog_group_commit_sync_no_delay_count的值,就会触动一次组提交。
3.2MySQL5.7多线程复制的实现
MySQL期望最大化的还原主库的并行度,实现方式是在binlog event中增加必要的信息(last_committed和sequence_number),以便slave节点根据这些信息实现多线程复制。
MySQL5.7使用参数slave_parallel_type来兼容5.6的多线程复制;通过参数slave_parallel_workers指定多线程复制的线程数。
mysql> show variables like '%slave_parallel_type%';
+---------------------+---------------+
| Variable_name | Value |
+---------------------+---------------+
| slave_parallel_type | LOGICAL_CLOCK |
+---------------------+---------------+
1 row in set (0.01 sec)
mysql> show variables like '%slave_parallel_workers%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| slave_parallel_workers | 8 |
+------------------------+-------+
1 row in set (0.01 sec)
MySQL5.7的多线程复制建立在group commit的基础上,所有在主库上能够完成prepared的语句表示没有数据冲突,就可以在slave节点多线程复制。
四、MySQL事务提交方式及多线程复制分发
4.1MySQL5.7中事务提交方式
binlog的组提交是通过 Stage_manager 管理,其中比较核心内容如下:
class Stage_manager {
public:
enum StageID { // binlog的组提交包括了三个阶段
FLUSH_STAGE,
SYNC_STAGE,
COMMIT_STAGE,
STAGE_COUNTER
};
private:
Mutex_queue m_queue[STAGE_COUNTER];
};
binlog的组提交的三个阶段主要执行的工作内容为:
InnoDB, Prepare
SQL已经成功执行并生成了相应的redo和undo内存日志;
Binlog, Flush Stage
所有已经注册线程都将写入binlog缓存;
Binlog, Sync Stage
binlog缓存将sync到磁盘,sync_binlog=1时该队列中所有事务的binlog将永久写入磁盘;
InnoDB, Commit stage
leader根据顺序调用存储引擎提交事务;
每个阶段都有各自的队列,从而使每个会话的事务进行排队,提高并发性能。如果当一个线程注册到一个空队列时,该线程就做为该队列的leader,后注册到该队列的线程均为follower,后续的操作都由leader控制队列中follower行为。
leader同时会带领当前队列的所有follower到下一个阶段 去执行,当遇到下一个阶段为非空队列时,leader会变成follower注册到此队列中。
4.2多线程复制分发原理
当slave_parallel_workers参数设置成n时,会有n个worker线程,由它来执行event,原来的sql线程变成coordinator线程,由它来读取relay log,并按照一定规则将读到的event分配给worker线程执行。从库是以事务为单位进行APPLY的,每一个事务有一个GTID事件,都有一个last_committed和sequence_number,多线程复制分发原理如下:
1、从库SQL线程读取一个新事务,拿出last_committed和sequence_number的值。
2、判断当前last_committed是否大于当前已经执行的sequence_number的最小值。如果大于,则说明上一组事务还没有完成,需要等待last_committed等于sequence_number后继续;否则说明当前事务和正在执行在同一组,直接继续。
3、SQL线程寻找worker线程,将当前事务交给worker,worker线程去APPLY这个事务。
五、总结
多线程复制从MySQL5.6到MySQL5.7的发展,真正地实现多线程复制,解决了复制延迟问题!
上一篇: PHP实现异步的三种方式
下一篇: Mysql之多源复制