MySQL 26 备库为什么会延迟好几个小时
上一篇文章介绍的场景中,对备库延迟的影响一般是分钟级的。但如果备库执行日志的速度持续低于主库生成日志的速度,那这个延迟就可能成了小时级别。这就涉及今天介绍的话题:备库并行复制能力。
主备流程图:

主备的并行复制能力主要是上图的两个黑色箭头,一个箭头代表客户端写入主库,另一个箭头代表备库上sql_thread执行中转日志。
主库上影响并发度的原因是各种锁,由于InnoDB支持行锁,除了所有并发事务都在更新同一行这种极端场景外,对业务并发度的支持总体良好。备库上影响并发度是sql_thread更新数据的逻辑,在单线程的情况下会导致备库应用日志不够快,造成主备延迟。
从单线程复制到最新版本的多线程复制,中间的演化经历了好几个版本。多线程复制实际上就是把sql_thread拆成多个线程:

coordinator就是原来的sql_thread,不过现在不再直接更新数据,只负责读取中转日志和分发事务,真正更新日志的变成了worker线程,线程个数由参数slave_paraller_workers决定。
coordinator在分发时需要满足两个基本要求:
更新同一行的两个事务,必须被分发到同一个worker中。不然由于CPU调度策略,后面的事务肯定比前面的事务先执行,会导致主备不一致;
同一个事务不能被拆开,必须放到同一个worker中。不然比如一个事务更新了表t1和t2中各一行,如果表t1执行完的瞬间备库上有一个查询,就会看到事务更新一半的结果,破坏了事务逻辑的隔离性。
各版本的多线程复制都遵循了这两条基本原则。
MySQL 5.5版本的并行复制策略
官方5.5版本不支持并行复制,这里主要介绍两种并行策略。
按表分发策略
如果两个事务更新不同的表,就可以并行。如果有跨表的事务,还是需要把两张表放在一起考虑:

可以看到,每个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中有一个事务涉及到db1.t2表。
假设此时coordinator从中转日志读入一个新事务T,T修改的行涉及到表t1和t3,那么分配流程为:
由于事务T涉及修改表t1,而worker_1队列有事务在修改表t1,事务T和队列中的某个事务要修改同一个表的数据,事务T和worker_1是冲突的;
因此,T和worker_2也冲突;
事务T和多于一个worker冲突,coordinator线程进入等待;
每个worker继续执行,假设hash_table_2中涉及修改t3的事务先执行完成,就会把db1.t3这一项去掉;
coordinator发现跟事务T冲突的worker只有worker_1,会把它分配给worker_1;
coordinator继续读下一个中转日志,继续分配事务。
即事务分发时跟所有worker的冲突关系包括三种情况:
如果和所有worker都不冲突,coordinator线程会把这个事务分配给最空闲的worker;
如果和多于一个worker冲突,coordinator线程就进入等待状态,直到和这个事务存在冲突关系的worker只剩下1个;
如果只和一个worker冲突,coordinator线程会把这个事务分配给这个存在冲突关系的worker。
按表分发策略在多个表负载均衡的场景里效果很好,但如果碰到所有更新事务都涉及到某个表的时候,所有事务都会被分配到同一个worker中,就退化成单线程复制。
按行分发策略
要解决热点表的并行复制问题就需要一个按行并行复制的方案,其核心思路是:如果两个事务没有更新相同的行,它们在备库上可以并行执行。
这时判断一个事务T和worker是否冲突,用的规则就从“修改同一表”变成了“修改同一行”。
按行复制的数据结构也类似,是为每个worker分配一个hash表,其中key是“库名+表名+唯一键的值”,比如对于表和流程:
CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `a` (`a`)
) ENGINE=InnoDB;
insert into t1 values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5);

那么对于update t1 set a=1 where id=2的语句,事务hash表就有3个项:
key=hash_func(db1+t1+"PRIMARY"+2), value=2,value=2是因为修改前后的id值不变,出现了两次;key=hash_func(db1+t1+"a"+2), value=1,表示会影响到这个表a=2的行;key=hash_func(db1+t1+"a"+1), value=1,表示会影响到这个表a=1的行;
可见,相比于按表并行分发策略,按行并行策略在决定线程分发的时候,需要消耗更多的计算资源。
按行分发策略的并行度更高,但是如果是要操作很多行的大事务,该策略有两个问题:
耗费内存,比如语句要删除100万行数据,这时候hash表就要记录100万个项;
耗费CPU,解析binlog然后计算hash值对于大事务来说成本还是很高的。
因此,这时候一般会设置一个阈值,单个事务如果超过设置的行数阈值,就暂时退化为单线程模式,退化过程的逻辑:
coordinator暂时先hold住这个事务;
等待所有worker都执行完成变成空队列;
coordinator直接执行这个事务;
恢复并行。
这两个策略没有被合到官方,其目的主要是抛砖引玉,方便理解后面介绍的社区版本策略。
MySQL 5.6版本的并行复制策略
官方5.6版本支持了并行复制,支持的粒度是按库并行,相当于上面用于决定分发策略hash表里,key是数据库名。
该策略的并行效果取决于压力模型,如果主库上有多个DB,且各个DB的压力均衡,使用这个策略的效果很好。相比于按表和按行,该策略有两个优势:
构造hash值很快,只需要库名;
不要求binlog的格式,statement格式的binlog也可以很容易拿到库名。
但如果主库的表都放在同一个DB里,或不同DB的热点不同,比如一个业务逻辑库一个系统配置库,那就起不到并行效果。
MariaDB的并行复制策略
MariaDB并行复制策略利用的就是redo log组提交优化的特性:
能够在同一组里提交的事务,一定不会修改同一行;
主库上可以并行执行的事务,备库上也一定是可以并行执行的。
其实现:
在一组里一起提交的事务,有一个相同的commit_id,下一组就是commit_id+1;
commit_id直接写到binlog;
传到备库应用时,相同commit_id的事务会分发到多个worker执行;
这一组全部执行完成后,coordinator再取下一批。
该策略的问题是,并没有真正实现模拟主库并发度的目标。假如有三组事务,那么主库执行情况为:

而备库执行情况为:

可以看到,在备库上执行的时候,要等第一组事务完全执行完成后,第二组事务才能开始执行,这样系统的吞吐量就不够。
此外该方案还容易被大事务拖后腿,假设trx2是大事务,那么即使trx1和trx3完成,也需要等待trx2完成后才能开始执行下一组。
MySQL 5.7版本的并行复制策略
5.7版本提供了类似MariaDB的功能,由参数slave-parallel-type来控制并行复制策略:
配置为DATABASE,表示使用MySQL 5.6版本的按库并行策略;
配置为LOGICAL_CLOCK,表示的是类似MariaDB的策略,不过针对并行度做了优化。
MariaDB的核心是“所有处于commit”状态的事务可以并行,但同时处于“执行状态”的所有事务是不能并行的,因为事务处于commit状态表示已经通过锁冲突的检验。但在两阶段提交过程中,实际上只要能达到redo log prepare,就表示事务已经通过锁冲突检验。

因此5.7并行复制策略思想是:
同时处于prepare状态的事务,在备库执行时是可以并行的;
处于prepare状态的事务,与处于commit状态的事务之间,在备库执行时也是可以并行的。
MySQL 5.7.22的并行复制策略
5.7.22版本增加了一个新的策略——基于WRITESET的并行复制。新增参数binlog-transaction-dependency-tracking来控制是否启用新策略。参数的值有三种:
COMMIT_ORDER,表示根据同时进入prepare和commit来判断是否可以并行;
WRITESET,表示对于事务涉及更新的每一行,计算出这一行的hash值组成集合writeset,如果两个事务的writeset没有交集就可以并行;
WRITESET_SESSION,在WRITESET基础上多了一个约束,即主库上同一个线程先后执行的两个事务,在备库执行时要保证相同的先后顺序。
hash值是通过“库名+表名+索引名+值”计算出来的。
该策略和最前面的按行分发策略类似,但还是有一定优势:
writeset是在主库生成后直接写入到binlog里的,这样在备库执行时不需要解析binlog内容;
不需要把整个事务的binlog都扫一遍才能确定分发到哪个worker,更省内存;
备库分发策略不依赖binlog内容,binlog可以是statement格式。
MySQL 26 备库为什么会延迟好几个小时的更多相关文章
- MySQL主备库切换(MHA)演练与总结
演练包括被动切换和主动切换两部分.被动切换是主库宕机,主动切换是人工手动触发. 演练步骤大致如下: 1 先停掉主库,模拟主库宕机 2 mha将vip切到备库,备库变成主库, ...
- Mysql复制-Slave库设置复制延迟
mysql> stop slave; mysql> change master to master_delay=10;#单位是秒 mysql> start slave; mysql& ...
- MySQL · 答疑解惑 · 备库Seconds_Behind_Master计算
背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...
- 备库Seconds_Behind_Master的计算
背景 在mysql主备环境下,主备同步过程如下,主库更新产生binlog, 备库io线程拉取主库binlog生成relay log.备库sql线程执行relay log从而保持和主库同步. 理论上主库 ...
- 请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间【转】
本文来自:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之 ...
- 请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间
链接:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之间的 ...
- mysql之 误用SECONDS_BEHIND_MASTER衡量MYSQL主备的延迟时间
链接:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之间的 ...
- MySQL主备同步延迟
今天看到mycat的日志,发现在wrapper.log中频繁warning:slave延迟23006秒 查看数据指标: show engine innodb status正常,而且slave的拷贝点在 ...
- mysql数据库从库同步延迟的问题
在从服务器上执行show slave status;可以查看到很多同步的参数,我们需要特别注意的参数如下,希望文章对各位会有所帮助. 在从服务器上执行show slave status;可以查看到很多 ...
- 【转载】mysql主键的缺少导致备库hang
最近线上频繁的出现slave延时的情况,经排查发现为用户在删除数据的时候,由于表主键的主键的缺少,同时删除条件没有索引,或或者删除的条件过滤性极差,导致slave出现hang住,严重的影响了生产环境的 ...
随机推荐
- MCP Server On FC 之旅第四站: 长连接闲置计费最高降低87%成本的技术内幕
函数计算( FC )是阿里云事件驱动的全托管计算服务, 使用函数计算,您无需采购与管理服务器等基础设施,只需编写并上传代码或镜像.函数计算为您准备好计算资源,弹性地.可靠地运行任务,并提供日志查询.性 ...
- Java 当文件不存在时自动创建文件目录和文件
操作文件流的时候,经常遇到在新目录中创建文件的场景,因此,这里记录如何判断文件是否存在,如果不存在,则如何创建目录和文件. public static void main(String[] args) ...
- MySQL SQL语句书写顺序和执行顺序
目录 SQL语句书写顺序和执行顺序 MySql执行顺序理解 实例 知识扩展 on和where的区别 limit 分页 结束语 Reference SQL语句书写顺序和执行顺序 (7) SELECT ( ...
- 2021NOI 省选训练赛day2T1 A. 黑白沙漠
2021NOI 省选训练赛day2T1 A. 黑白沙漠 Problem 在一条长度有限的数轴 \([L,R]\) 上,有 \(N\) 栋建筑物.其中第 \(i\) 栋建筑物的坐标为 \(x_i\) , ...
- linux系统磁盘快速创建多个分区
# 创建分区 [root@centos7 ~]# cat parted.sh parted --script /dev/sdb \ mklabel gpt \ mkpart primary 1MiB ...
- ChatGPT学习之旅 (5) 终极Prompt设计
大家好,我是Edison. 上一篇:Prompt花式用法 AI时代的处世法则,能动口,尽量不动手!通过前面4篇内容我们了解了基本的Prompt用法,今天我们来学习终极的Prompt设计方法! 前提条件 ...
- react-router v6 路由
前言 之前写react的时候 路由真的是难用,这次更新后,极为好用! 嵌套路由 main.jsx import './index.css'; import * as React from 'react ...
- 一、webrtc版本接听视频电话-纯js版
先看效果 用户1--拨打 用户2–接听 前端代码 index.html <!DOCTYPE html> <html lang="en"> <head& ...
- Faiss能解决什么问题?Faiss是什么?
1. Faiss能解决什么问题? 为了比较两个结构化的数据是否相似,例如两张图片是不是类似,两段文本表达的含义是否类似,则需要将非结构化的数据先转成向量数据,然后再进行相似度比较. 如何通过向量化技术 ...
- wordpress - 上传附件大小更改
我用的是树莓派4b,需要修改php.ini的路径是/etc/php/7.3/apache2/php.ini.根据安装的php版本来决定所在的路径. 让然了,也可以 sudo find / -name ...