导致备库延迟的原因。偶发性查询、备份,备库延迟影响分钟级,能追上来。
备库执行日志的速度持续低于主库生成日志的速度,延迟小时级别。压力持续高主库来说,永远都追不上
1、备库并行复制能力
黑色的两个箭头:写入主库(并行度高),备库上 sql_thread 执行中转日志(relay log)。
主库:,各种锁影响并发度。支持行锁,除极端场,并发度好。并发压测线程 32 就比单线程时,总吞吐量高。
备库: sql_thread 更新数据 (DATA) 的逻辑。单线程主备延迟(5.6 之前),主库并发高、TPS 高严重主备延迟
2、复制机制:sql_thread拆成多个线程
coordinator 就是sql_thread, 不直接更新,只读中转日志和分发事务。真正更新日志worker 线程。个数slave_parallel_workers 8~16 之间最好(32 核物理机),备库提供读查询,不能把 CPU 都吃光。
3、coordinator 分发两个基本要求:
1)不能更新覆盖。更新同一行两个事务,分发同一worker 中。
例:第二个比第一个事务先执行。主、备执行顺序相反,不一致
2) 同一事务放同一个 worker 中。
例:事务更新了表 t1 、2 各一行,分到不同 worker ,最终结果主备一致的, t1 执行完成瞬间,备库上有一个查询,看到事务“更新了一半的结果”,破坏隔离性。
一、MySQL 5.5 并行复制策略
不支持并行复制的。备库单线程复制。
1.1按表分发策略
两个事务更新不同表,可并行。如是跨表事务,放一起考虑。
每个 worker 线程对应一个 hash 表,保存当前正在这个 worker “执行队列”事务所涉及的表。hash 表 key 是“库名. 表名”,value 数字,队列多少个事务修改表。
有事务分配给 worker 时,事务里面涉及的表会被加到对应的 hash 表中。worker 执行完成后,这个表会被从 hash 表中去掉。
图 3 中,hash_table_1 表示,现在 worker_1 的“待执行事务队列”里,有 4 个事务涉及到 db1.t1 表,有1 涉及到 db2.t2 表;hash_table_2 worker_2 有一个事务会更新到表 t3 的数据。
coordinator 从中转日志中读入一个新事务 T,这个事务修改的行涉及到表 t1 和 t3。
事务 T 的分配流程,看分配规则。
1. 由于事务 T 中涉及修改表 t1,worker_1 队列中有事务在修改表 t1,事务 T 和队列中的某个事务要修改同一个表的数据,冲突。
2. 顺序判断事务 T 和每个 worker 队列的冲突关系,事务 T 跟 worker_2 也冲突。
3. T 跟多于一个 worker 冲突,coordinator 等待。
4. 每个 worker 继续执行,同时修改 hash_table。假设 hash_table_2里面涉及到修改表 t3 的事务先执行完成,从 hash_table_2中把 db1.t3 这一项去掉。
5. 冲突的 worker 只有 worker_1 了,因此就把它分配给 worker_1。
6. coordinator 读下一个中转日志,分配事务。
worker 冲突关系包括以下三种情况:
1. 跟所有 worker 都不冲突,coordinator 分配给最空闲woker;
2. 跟多于一个 worker 冲突,coordinator 等待状态,直到只剩1 个;
3. 只跟一个 worker 冲突,分配给这个 worker。
负载均匀效果好。热点表,涉及某一个表的时候,所有事务都会被分配到同一个 worker 中,单线程复制。
1.2按行分发策略
没更新相同行,备库可并行。 binlog 格式 row。
这时候,我们判断一个事务 T 和 worker 是否冲突,用的就规则就不是“修改同一个表”,而是“修改同一行”。
数据结构差不多,为每个 worker, hash 表。key,“库名 + 表名 + 唯一键值”。
唯一键:除了主键,还有唯一索引 a:主库执行这两个事务:
更新的行主键值不同,分到不同的 worker,B 先执行。 id=1 行 a 值还是 1,唯一键冲突。
因此,基于行的策略,事务 hash 表中还需要考虑唯一键, key “库名 + 表名 + 索引 a 的名字 +a 的值”。
表 t1 上执行 update t1 set a=1 where id=2 语句, binlog 记录行修改前后各个字段值。
coordinator在解析这个语句的 binlog 时,hash 表三个项:
1. key=hash_func(db1+t1+“PRIMARY”+2), value=2; 修改前后行 id 值不变,出现两次
2. key=hash_func(db1+t1+“a”+2), value=1,影响表 a=2 行
3. key=hash_func(db1+t1+“a”+1), value=1,影响表 a=1 行
按行并行,消耗更多计算资源。约束条件:
1. 从 binlog 里解析出表名、主键值和唯一索引的值。主库 binlog 格式必须row;
2. 必须有主键;
3. 不能有外键。级联更新行不记录 binlog 中,冲突检测不准确。
按行并行度高。大事务的话,两个问题:
1. 耗内存。删除 100 万行, hash 表记录 100 万项。
2. 耗CPU。解析 binlog,计算 hash 值,成本高。
设置一个阈值,单个事务如果超过设置的行数阈值(比如,如果单个事务更新的行数超过 10 万行),退化为单线程模式:
1. coordinator 暂时 hold 住事务;
2. 所有 worker 执行完,成空队列;
3. coordinator 直接执行这个事务; 恢复并行模式。
二、MySQL 5.6 版本并行复制策略
并行复制,按库并行。理解了上面介绍的按表分发策略和按行分发策略,你就理解了,用于决定分发策略的 hash 表里,key 就是数据库名。
取决于压力模型。多个 DB压力均衡,效果好。
优势:
1. 构造 hash 值快,只要库名;实例上 DB 数不多,不会出现需要构造 100 万个项这种情况。
2. 不要求 binlog 格式。statement 格式binlog 很容易拿到库名。
没效果:主库上的表都放在同一个 DB 里面,;不同 DB 热点不同,一个是业务逻辑库,一个是系统配置库
创建不同的 DB,把相同热度的表均匀分到这些不同的 DB 中,强行用。由于需要特地移动数据,用得并不多。
三、MariaDB 的并行复制策略
在第 23 篇文章中, redo log 组提交 (group commit) 优化, MariaDB 并行复制就是这个特性:
1. 同一组里提交的事务,不修改同一行;
2. 主库上并行,备库也可。
MariaDB 是这么做的:
1. 组里一起提交事务,相同commit_id
2. commit_id 直接写binlog 里;
3. 传备库时,相同 commit_id 分发多个 worker 执行;
4. 执行完,coordinator 取下一批,commit_id+1。
之前都是“分析 binlog,拆分到 worker”上。MariaDB “模拟主库并行模式”。
问题:备库没真正并行。主库trx1、trx2 和 trx3 提交时,trx4、trx5 和 trx6 在执行的。提交完,下一组很快 commit
MariaDB 并行复制策略,备库上执行如图 6
备库上,第一组完成,第二组才开始,吞吐量不够。
容易被大事务拖后腿。trx2超大事务,备库trx1 和 trx3 执行完成后,只能等 trx2完成,只有worker 工作,浪费。
四、MySQL 5.7 的并行复制策略
类似功能,参数slave-parallel-type 控制并行复制策略:
1. DATABASE, 5.6 并行策略;
2. LOGICAL_CLOCK,MariaDB 。做优化
“执行状态”所有事务,不能同时并行,锁冲突而等待,备库分配不同worker,不一致。
redo log prepare、 commit 状态,表示已通过锁冲突。并行思想:
1. 同时prepare 状态,备库可并行;
2. prepare 、 commit 之间,备库也可并行。
binlog 组提交两个参数,主库提交慢,让备库快。提升备库复制并发度:
1. binlog_group_commit_sync_delay 延迟多少微秒后才调用fsync;
2. binlog_group_commit_sync_no_delay_count 累积多少次调用 fsync。
五、MySQL 5.7.22 并行复制策略
基于WRITESET 并行复制。增加binlog-transaction-dependency-tracking控制是否启用新策略。参数三种:
1. COMMIT_ORDER,同时进入 prepare 和 commit 判断可否并行
2. WRITESET,更新每行,计算 hash 值,组成集合 writeset。两个事务没有操作相同的行,writeset 没交集,可并行。
3. WRITESET_SESSION,WRITESET 多了约束,主库先后执行两事务,备库相同顺序。
唯一标识 hash 值:“库名 + 表名 + 索引名 + 值”。还有其他唯一索引,每个唯一索引,insert 对应 writeset 多增加 hash 值。
优势:
1. writeset 主库生成后写入binlog 面,备库不需解析 binlog(event 里的行数据),省计算量;
2. 不需扫一遍binlog 决定分发到哪个 worker,省内存;
3. 备库分发策略不依赖binlog ,statement 也可
通用有保证。“表上没主键”和“外键约束”场景,WRITESET 策略没法并行,暂时退化为单线程
小结
MySQL各种多线程复制策略。不同策略优缺点
为什么要有多线程复制呢?单线程复制能力低,更新压力大主库,备库追不上主库。现象:备库seconds_behind_master 值越来越大
大事务不仅会影响主库,备库复制延迟。建议大事务拆小
MySQL5.7 备库并行策略,修改binlog,并不是向上兼容的,在主备切换、版本升级的时候需要把这个因素也考虑进去。
思考题
MySQL 5.7.22版本主库,单线程插入很多,3 小时后,给主库搭相同备库
更快追上主库,并行复制。binlog-transaction-dependency-tracking 参数 COMMIT_ORDER、WRITESET 和WRITE_SESSION 选择哪个?原因是?另外两个现象?
WRITESET。其他两个单线程。
评论1
同行更新几个事务, commit_id 相同,备库并行执行不一致?
同一行事务不可能同时commit。