Mysql主备-备库并行复制能力

如果备库执行日志的速度持续低于主库生成日志的速度,那这个延迟就有可能成了小时级别。而且对于一个压力持续比较高的主库来说,备库很可能永远都追不上主库的节奏。

主备流程

在这里插入图片描述
图中黑色的两个箭头。一个箭头代表了客户端写入主库,另一箭头代表的是备库上sql_thread执行中转日志(relay log)

日志在备库上的执行,就是图中备库上sql_thread更新数据(DATA)的逻辑。如果是用单线程的话,就会导致备库应用日志不够快,造成主备延迟。

在官方的5.6版本之前,MySQL只支持单线程复制,由此在主库并发高、TPS高时就会出现严重的主备延迟问题。

从单线程复制到最新版本的多线程复制,中间的演化经历了好几个版本。

多线程复制机制

在这里插入图片描述

图2中,coordinator就是原来的sql_thread, 不过现在它不再直接更新数据了,只负责读取中转日志和分发事务。真正更新日志的,变成了worker线程。而work线程的个数,就是由参数slave_parallel_workers决定的。把这个值设置为8~16之间最好(32核物理机的情况),毕竟备库还有可能要提供读查询,不能把CPU都吃光了。

问题1:事务能不能按照轮询的方式分发给各个worker,也就是第一个事务分给worker_1,第二个事务发给worker_2呢?

不行。事务被分发给worker以后,不同的worker就独立执行了。但是,由于CPU的调度策略,很可能第二个事务最终比第一个事务先执行。而如果这时候刚好这两个事务更新的是同一行,也就意味着,同一行上的两个事务,在主库和备库上的执行顺序相反,会导致主备不一致的问题。

问题2:同一个事务的多个更新语句,能不能分给不同的worker来执行呢?

不行。举个例子,一个事务更新了表t1和表t2中的各一行,如果这两条更新语句被分到不同worker的话,虽然最终的结果是主备一致的,但如果表t1执行完成的瞬间,备库上有一个查询,就会看到这个事务“更新了一半的结果”,破坏了事务逻辑的隔离性。

coordinator在分发的时候,需要满足以下这两个基本要求:
1.不能造成更新覆盖。这就要求更新同一行的两个事务,必须被分发到同一个worker中。
2.同一个事务不能被拆开,必须放到同一个worker中。

MySQL 5.5版本的并行复制策略

官方MySQL 5.5版本是不支持并行复制的。(下面介绍的是丁奇团队(阿里?)自己实现的并行复制策略思路)

(1)按表分发策略

按表分发事务的基本思路是,如果两个事务更新不同的表,它们就可以并行。因为数据是存储在表里的,所以按表分发,可以保证两个worker不会更新同一行。如果有跨表的事务,还是要把两张表放在一起考虑的。

每个worker线程对应一个hash表,用于保存当前正在这个worker的“执行队列”里的事务所涉及的表。hash表的key是“库名.表名”,value是一个数字,表示队列中有多少个事务修改这个表。
在有事务分配给worker时,事务里面涉及的表会被加到对应的hash表中。worker执行完成后,这个表会被从hash表中去掉。

hash_table_1表示,现在worker_1的“待执行事务队列”里,有4个事务涉及到db1.t1表,有1个事务涉及到db2.t2表;hash_table_2表示,现在worker_2中有一个事务会更新到表t3的数据。在这里插入图片描述

假设在图中的情况下,coordinator从中转日志中读入一个新事务T,这个事务修改的行涉及到表t1和t3。

1.由于事务T中涉及修改表t1,而worker_1队列中有事务在修改表t1,事务T和队列中的某个事务要修改同一个表的数据,这种情况我们说事务T和worker_1是冲突的。
2.顺序判断事务T和每个worker队列的冲突关系,会发现事务T跟worker_2也冲突。
3.事务T跟多于一个worker冲突,coordinator线程就进入等待。
4.每个worker继续执行,同时修改hash_table。假设hash_table_2里面涉及到修改表t3的事务先执行完成,就会从hash_table_2中把db1.t3这一项去掉。这样coordinator会发现跟事务T冲突的worker只有worker_1了,因此就把它分配给worker_1。
5.coordinator继续读下一个中转日志,继续分配事务。

每个事务在分发的时候,跟所有worker的冲突关系包括以下三种情况:
1.如果跟所有worker都不冲突,coordinator线程就会把这个事务分配给最空闲的woker;
2.如果跟多于一个worker冲突,coordinator线程就进入等待状态,直到和这个事务存在冲突关系的worker只剩下1个;
3.如果只跟一个worker冲突,coordinator线程就会把这个事务分配给这个存在冲突关系的worker。

这个按表分发的方案,在多个表负载均匀的场景里应用效果很好。但是,如果碰到热点表,比如所有的更新事务都会涉及到某一个表的时候,所有事务都会被分配到同一个worker中,就变成单线程复制了。

(2)按行分发策略

要解决热点表的并行复制问题,就需要一个按行并行复制的方案。按行复制的核心思路是:如果两个事务没有更新相同的行,它们在备库上可以并行执行。
这时候,我们判断一个事务T和worker是否冲突,用的就规则就不是“修改同一个表”,而是“修改同一行”。
基于行的策略,事务hash表中还需要考虑唯一键,即key应该是“库名+表名+索引a的名字+a的值”。

相比于按表并行分发策略,按行并行策略在决定线程分发的时候,需要消耗更多的计算资源。

如果是要操作很多行的大事务的话,按行分发的策略有两个问题:
1.耗费内存。比如一个语句要删除100万行数据,这时候hash表就要记录100万个项。
2.耗费CPU。解析binlog,然后计算hash值,对于大事务,这个成本还是很高的。

在实现这个策略的时候会设置一个阈值,单个事务如果超过设置的行数阈值(比如,如果单个事务更新的行数超过10万行),就暂时退化为单线程模式。

退化过程的逻辑大概是这样的:
1.coordinator暂时先hold住这个事务;
2.等待所有worker都执行完成,变成空队列;
3.coordinator直接执行这个事务;
4.恢复并行模式;

MySQL 5.6版本的并行复制策略

官方MySQL5.6版本,支持了并行复制,只是支持的粒度是按库并行。

这个策略的并行效果,取决于压力模型。如果在主库上有多个DB,并且各个DB的压力均衡,使用这个策略的效果会很好。

相比于按表和按行分发,这个策略有两个优势:
1.构造hash值的时候很快,只需要库名;而且一个实例上DB数也不会很多,不会出现需要构造100万个项这种情况。
2.不要求binlog的格式。因为statement格式的binlog也可以很容易拿到库名。

但是,如果你的主库上的表都放在同一个DB里面,这个策略就没有效果了;或者如果不同DB的热点不同,比如一个是业务逻辑库,一个是系统配置库,那也起不到并行的效果。

MySQL 5.7的并行复制策略

官方的MySQL5.7版本也提供了类似的功能,由参数slave-parallel-type来控制并行复制策略:
1.配置为DATABASE,表示使用MySQL 5.6版本的按库并行策略;
2.配置为 LOGICAL_CLOCK,表示的就是类似MariaDB的策略。

MySQL 5.7并行复制策略的思想是:
1.同时处于prepare状态的事务,在备库执行时是可以并行的;
2.处于prepare状态的事务,与处于commit状态的事务之间,在备库执行时也是可以并行的。

MySQL 5.7.22的并行复制策略(基于WRITESET的并行复制)

新增了一个参数binlog-transaction-dependency-tracking,用来控制是否启用这个新策略。

这个参数的可选值有以下三种:
1.COMMIT_ORDER,表示的就是根据同时进入prepare和commit来判断是否可以并行的策略。
2.WRITESET,表示的是对于事务涉及更新的每一行,计算出这一行的hash值,组成集合writeset。如果两个事务没有操作相同的行,也就是说它们的writeset没有交集,就可以并行。
3.WRITESET_SESSION,是在WRITESET的基础上多了一个约束,即在主库上同一个线程先后执行的两个事务,在备库执行的时候,要保证相同的先后顺序。

这跟我们前面介绍的基于MySQL 5.5版本的按行分发的策略是差不多的。

不过,MySQL官方的这个实现还是有很大的优势:
1.writeset是在主库生成后直接写入到binlog里面的,这样在备库执行的时候,不需要解析binlog内容(event里的行数据),节省了很多计算量;
2.不需要把整个事务的binlog都扫一遍才能决定分发到哪个worker,更省内存;
3.由于备库的分发策略不依赖于binlog内容,所以binlog是statement格式也是可以的。

思考题:

假设一个MySQL 5.7.22版本的主库,单线程插入了很多数据,过了3个小时后,我们要给这个主库搭建一个相同版本的备库。
这时候,你为了更快地让备库追上主库,要开并行复制。在binlog-transaction-dependency-tracking参数的COMMIT_ORDER、WRITESET和WRITE_SESSION这三个取值中,你会选择哪一个呢?你选择的原因是什么?如果设置另外两个参数,你认为会出现什么现象呢?

答:应该将这个参数设置为WRITESET。由于主库是单线程压力模式,所以每个事务的commit_id都不同,那么设置为COMMIT_ORDER模式的话,从库也只能单线程执行。
同样地,由于WRITESET_SESSION模式要求在备库应用日志的时候,同一个线程的日志必须与主库上执行的先后顺序相同,也会导致主库单线程压力模式下退化成单线程复制。

[资料来源]
1.Mysql实战45讲-丁奇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL 8从并行复制是一种新的复制方式,可以提高复制性能。下面是MySQL和从的配置步骤: 1. 配置 在的my.cnf配置文件中添加以下参数: ``` binlog_format=ROW binlog_row_image=FULL gtid_mode=ON enforce_gtid_consistency=ON log_slave_updates=ON ``` 2. 从配置 在从的my.cnf配置文件中添加以下参数: ``` gtid_mode=ON enforce_gtid_consistency=ON binlog_format=ROW log_slave_updates=ON slave_parallel_workers=4 slave_parallel_type=LOGICAL_CLOCK ``` 其中,slave_parallel_workers表示从使用多少个线程进行并行复制,slave_parallel_type表示并行复制的算法,这里选择了LOGICAL_CLOCK。 3. 和从创建用户 在和从中创建一个用于复制的用户,并赋予REPLICATION SLAVE的权限。 4. 份 在上执行份命令,例如: ``` mysqldump --all-databases --master-data > backup.sql ``` 5. 从恢复 将份文件拷贝到从上,执行以下命令进行恢复: ``` mysql < backup.sql ``` 6. 从连接 在从上执行以下命令连接: ``` CHANGE MASTER TO MASTER_HOST='master_ip', MASTER_USER='repl', MASTER_PASSWORD='repl_password', MASTER_AUTO_POSITION=1; ``` 其中,MASTER_HOST为的IP地址,MASTER_USER和MASTER_PASSWORD为创建的复制用户的用户名和密码,MASTER_AUTO_POSITION表示使用GTID进行复制。 7. 启动从复制 在从上执行以下命令启动复制: ``` START SLAVE; ``` 至此,MySQL 8从并行复制配置就完成了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值