5.7新特性之多线程复制

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: MySQL5.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的发展,真正地实现多线程复制,解决了复制延迟问题!

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6天前
|
前端开发
请简述同步和异步的区别是什么
请简述同步和异步的区别是什么
16 2
|
2月前
|
存储 Linux API
C++新特性 线程局部存储
C++新特性 线程局部存储
|
8月前
多线程的三种实现代码
多线程的三种实现代码
50 0
|
8月前
|
缓存 安全 Java
|
SQL 缓存 关系型数据库
MySQL的延迟复制和异步复制的区别是什么?底层原理是什么?
MySQL的延迟复制和异步复制的区别是什么?底层原理是什么?
|
Java
Java实现多线程的两种方式
run():封装线程执行的代码,直接调用,相当于普通方法的调用
96 0
Java实现多线程的两种方式
|
存储
多线程原理和实现方式
多线程原理和实现方式
154 1
|
缓存 NoSQL Linux
Redisv6.0为何引入多线程
Redisv6.0为何引入多线程
|
Java
java实现多线程的三种方式
java实现多线程的三种方式
84 0
线程的3种实现方式并深入源码简单分析实现原理(3)
线程的3种实现方式并深入源码简单分析实现原理(3)
118 0
线程的3种实现方式并深入源码简单分析实现原理(3)