MySQL -- 从库并行复制

栏目: 数据库 · 发布时间: 5年前

MySQL -- 从库并行复制

  1. 第一个黑色箭头:客户端写入主库,第二个黑色箭头:从库上 sql_thread 执行 relaylog ,前者的并发度大于后者
  2. 在主库上,影响并发度的原因是 ,InnoDB支持 行锁 ,对业务并发度的支持还算比较友好
  3. 如果在从库上采用 单线程 (MySQL 5.6之前)更新 DATA 的话,有可能导致从库应用 relaylog 不够快,造成主从延迟

多线程模型

MySQL -- 从库并行复制

  1. coordinator 就是原来的 sql_thread ,但不会再直接应用 relaylog 后更新 DATA ,只负责 读取 relaylog 分发事务
  2. 真正更新日志的是 worker 线程,数量由参数 slave_parallel_workers 控制
mysql> SHOW VARIABLES LIKE '%slave_parallel_workers%';
+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| slave_parallel_workers | 4     |
+------------------------+-------+

分发原则

  1. 不能造成更新覆盖 ,更新同一行的两个事务,必须被分到同一个 worker
  2. 同一个事务不能被拆开 ,必须放到同一个 worker

并行复制策略

MySQL 5.5

按表分发策略

  1. 基本思路:如果两个事务更新的是不同的表,那么就可以并行
  2. 如果有 跨表的事务 ,还是需要将两张表放在一起考虑

具体逻辑

MySQL -- 从库并行复制

  1. 每个 worker 线程对应一个 hash 表,用于保存当前正在这个 worker 的执行队列里的事务所涉及的表
    • key库名.表名value 是一个数字,表示队列中有多少事务修改这个表
  2. 在有事务分配给 worker 时,事务里面涉及到的表会被加到对应的 hash 表中
  3. worker 执行完成后,这个表会从 hash 表中去掉
  4. hash_table_1 表示:现在 worker_1待执行事务队列 中,有4个事务涉及到 db1.t1 ,有1个事务涉及到 db2.t2
  5. hash_table_2 表示:现在 worker_2待执行事务队列 中,有1个事务涉及到 db1.t3
  6. 现在 coordinatorrelaylog 中读入一个事务 T ,该事务修改的行涉及到 db1.t1db1.t3
  7. 分配流程
    • 事务 T 涉及到修改 db1.t1 ,而 worker_1 的队列中有事务在修改 db1.t1 ,事务 Tworker_1 是冲突的
    • 按照上面的逻辑,事务 Tworker_2 也是冲突的
    • 事务 T多于1worker 冲突, coordinator 线程进入 等待
    • 每个 worker 继续执行,同时会修改 hash_table
      • 假设 hash_table_2 里涉及到修改 db1.t3 先执行完, hash_table_2 会把 db1.t3 去掉
    • coordinator 发现跟事务 T 冲突的只有 worker_1 ,因此直接将事务 T 分配给 worker_1
    • coordinator 继续读取下一个 relaylog ,继续分发事务

冲突关系

  1. 如果事务与所有 worker 都不冲突coordinator 线程就会把该事务分发给 最空闲worker
  2. 如果事务跟 多于1个 worker 冲突, coordinator 线程就会进入 等待 状态,直到和该事务存在冲突关系的 worker 只剩下一个
  3. 如果事务只跟 1个 worker 冲突, coordinator 线程就会把该事务分发给该 worker

小结

  1. 适用于在 多个表负载均匀 的场景
  2. 如果碰到 热点表 ,有可能 退化为单线程复制

按行分发策略

  1. 核心思路:如果两个事务没有更新 相同的行 ,它们是可以在从库上 并行执行
  2. 要求: binlog 必须采用 ROW 格式
  3. 事务 Tworker 是否 冲突 的判断依据:修改 同一行
  4. 为每个 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);
session A session B
UPDATE t1 SET a=6 WHERE id=1;
UPDATE t1 SET a=1 WHERE id=2;
  1. 如果两个事务被分发到不同的 workersession B 的事务有可能先执行,报唯一键冲突错误
  2. 因此基于按行分发的策略,事务 hash 还需要考虑唯一键, key库名+表名+索引a的名字+a的值
  3. coordinator 在执行 UPDATE t SET a=1 WHERE id=2binlog 时,hash表的内容
    • key=hash_func(db1+t1+"PRIMARY"+2), value=2
      • value=2 :修改前后的行id值不变,出现了2次
    • key=hash_func(db1+t1+"a"+2), value=1
      • 影响到 a=2 的行
    • key=hash_func(db1+t1+"a"+1), value=1
      • 影响到 a=1 的行
  4. 相对于按表分发的策略,按行分发的策略在决定线程分发的时候,需要 消耗更多的计算资源

对比

  1. 按表分发或按行分发的约束
    • 能够从 binlog 解析出表名,主键值和唯一索引的值,因此必须采用 ROW 格式的 binlog
    • 必须有主键 ,因为 隐含主键 是不会在 binlog 中体现
    • 不能有外键 ,因为 级联更新的行 是不会记录在 binlog 中,这样冲突检测是不准确的
  2. 按行分发策略的并发度更高
  3. 如果操作很多行的大事务,按行分发策略的问题
    • 耗费内存 :如果要删除100W行数据,hash表就要记录100W个记录
    • 耗费CPU :解析 binlog ,然后计算 hash
    • 优化:设置 行数阈值 ,当单个事务超过设置的行数阈值,就退化为 单线程 模式,退化过程
      • coordinator 暂时先 hold 住这个事务
      • 等待 所有 worker执行完成 ,变成了空队列
      • coordinator 直接执行这个事务
      • 恢复并行模式

MySQL 5.6

  1. MySQL 5.6版本,支持粒度为 按库分发 的并行复制
  2. 在决定分发策略的 hash 表里, key数据库名
  3. 该策略的并行效果,取决于压力模型,如果各个DB的压力均匀,效果会很好
  4. 相比于 按表分发按行分发 ,该策略的两个优势
    • 构造 hash 值很快,只需要 数据库名 ,并且一个实例上DB数不会很多
    • 不要求 binlog 的格式,因为 STATEMENT 格式的 binlog 也很容易拿到 数据库名
  5. 如果主库上只有一个DB或者不同DB的热点不同,也起不到并行的效果

MariaDB

  1. MariaDB的并行复制策略利用了 redolog组提交 (group commit)
    • 能够在 同一组里提交的事务 ,一定 不会修改同一行
    • 主库 上可以 并行执行 的事务,在 从库 上也一定可以 并行执行
  2. 具体做法
    • 在一组里面提交的事务,有一个相同的 commit_id ,下一组就是 commit_id+1
    • commit_id 直接写到 binlog 里面
    • 传到 从库 应用的时候,相同 commit_id 的事务可以分发到多个 worker 上执行
    • 这一组全部执行完成后, coordinator 再去取下一批
  3. MariaDB目标: 模拟主库的并发行为
    • 问题:并没有真正的模拟主库并发度,在主库上,一组事务在 commit 的时候,下一组事务可以同时处于 执行中 的状态

主库并发事务

MySQL -- 从库并行复制

  1. 在主库上,在 trx1trx2trx3 提交的时候, trx4trx5trx6 是在执行
  2. 在第一组事务提交完成后,下一组事务很快就会进入 commit 状态

从库并发复制

MySQL -- 从库并行复制

  1. 在从库上,必须等第一组事务 完全执行 完成后,第二组事务才能开始执行,与主库相比, 吞吐量是下降的
  2. 并且很容易 被大事务拖后腿
    • 假设 trx2 是一个 超大事务trx1trx3 执行完成后,只能等 trx2 完全执行完成,下一组才能开始执行
    • 这段期间,只有一个 worker 线程在工作,是对资源的浪费

MySQL 5.7

  1. slave_parallel_type=DATABASE ,使用 MySQL 5.6的 按库分发 的并行复制策略
  2. slave_parallel_type=LOGICAL_CLOCK ,使用类似MariaDB的策略,但针对 并行度 做了优化
mysql> SHOW VARIABLES LIKE '%slave_parallel_type%';
+---------------------+----------+
| Variable_name       | Value    |
+---------------------+----------+
| slave_parallel_type | DATABASE |
+---------------------+----------+

LOGICAL_CLOCK

  1. 并不是所有处于 执行状态 的事务都可以并行的
    • 因为里面可能包括由于 锁冲突 而处于 锁等待状态 的事务
    • 如果这些事务在从库上被分配到不同的 worker ,会出现 主从不一致 的情况
  2. MariaDB的并行复制策略:所有处于 redolog commit 状态都事务是可以并行的
    • 事务处于 redolog commit 状态,表示已经 通过了锁冲突的检验
  3. MySQL 5.7的并行复制策略
    • 同时处于 redolog prepare fsync 状态的事务,在从库执行时是可以并行的
    • 处于 redolog prepare fsync 状态和 redolog commit 状态之间的事务,在从库上执行时也是可以并行的
  4. binlog_group_commit_sync_delaybinlog_group_commit_sync_no_delay_count
    • 故意拉长 binlogwritefsync 的时间,以此来减少 binlog 的写盘次数
    • 在MySQL 5.7,可以制造更多同时处于 redolog prepare fsync 阶段的事务,增加从库复制的并行度
    • 故意 让主库提交慢些让从库执行快些

只要达到 redolog prepare fsync 阶段,就已经表示事务已经通过了 锁冲突的检验

MySQL -- 从库并行复制

参考资料

《MySQL实战45讲》

转载请注明出处:http://zhongmingmao.me/2019/02/25/mysql-slave-parallel-replication/

访问原文「 MySQL -- 从库并行复制 」获取最佳阅读体验并参与讨论


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

暗时间

暗时间

刘未鹏 / 电子工业出版社 / 2011-7 / 35.00元

2003年,刘未鹏在杂志上发表了自己的第一篇文章,并开始写博客。最初的博客较短,也较琐碎,并夹杂着一些翻译的文章。后来渐渐开始有了一些自己的心得和看法。总体上在这8年里,作者平均每个月写1篇博客或更少,但从未停止。 刘未鹏说—— 写博客这件事情给我最大的体会就是,一件事情如果你能够坚持做8年,那么不管效率和频率多低,最终总能取得一些很可观的收益。而另一个体会就是,一件事情只要你坚持得足......一起来看看 《暗时间》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具