背景
TiDB 的一键程度伸缩个性,帮忙用户辞别了分库分表查问和运维带来的复杂度,然而在从分库分表计划切换到 TiDB 的过程中,这个复杂度转移到了数据迁徙流程里。TiDB DM 工具为用户提供了分库分表合并迁徙性能。
本篇文章将介绍 DM 外围处理单元 Sync,内容蕴含 binlog 读取、过滤、路由、转换,优化以及执行等逻辑。本文仅形容 DML 的解决逻辑,DDL 相干内容可参考《DM 分库分表 DDL“乐观协调”模式介绍》、《DM 分库分表 DDL“乐观协调”模式介绍》。
解决流程
从上图能够大抵理解到 Binlog replication 的逻辑解决流程
1. 从 MySQL/MariaDB 或者 relay log 读取 binlog events
2. 对 binlog events 进行解决转换
- Binlog Filter:依据 binlog 表达式过滤 binlog,通过
filters
配置 - Routing:依据“库 / 表”路由规定对“库 / 表”名进行转换,通过
routes
配置 - Expression Filter: 依据 SQL 表达式过滤 binlog,通过
expression-filter
配置
3. 对 DML 执行进行优化
- Compactor:将对同一条记录(主键雷同)的多个操作合并成一个操作,通过
syncer.compact
开启 - Causality:将不同记录(主键不同)进行冲突检测,散发到不同的 group 并发解决
- Merger:将多条 binlog 合并成一条 DML,通过
syncer.multiple-rows
开启
4. 将 DML 执行到上游
5. 定期保留 binlog position/gtid 到 checkpoint
优化逻辑
Compactor
DM 依据上游 binlog 记录,捕捉记录的变更并同步到上游,当上游对同一条记录短时间内做了屡次变更时(insert/update/delete),DM 能够通过 Compactor 将这些变更压缩成一次变更,缩小上游压力,晋升吞吐,如
INSERT + UPDATE => INSERT
INSERT + DELETE => DELETE
UPDATE + UPDATE => UPDATE
UPDATE + DELETE => DELETE
DELETE + INSERT => UPDATE
Causality
MySQL binlog 程序同步模型要求依照 binlog 程序一个一个来同步 binlog event,这样的程序同步势必不能满足高 QPS 低同步提早的同步需要,并且不是所有的 binlog 波及到的操作都存在抵触。
DM 采纳冲突检测机制,甄别进去须要程序执行的 binlog,在确保这些 binlog 的程序执行的根底上,最大水平地放弃其余 binlog 的并发执行来满足性能方面的要求。
Causality 采纳一种相似并查集的算法,对每一个 DML 进行分类,将互相关联的 DML 分为一组。具体算法可参考 TiDB Binlog 源码浏览系列文章(八)Loader Package 介绍 #并行执行 DML
Merger
MySQL binlog 协定,每条 binlog 对应一行数据的变更操作,DM 能够通过 Merger 将多条 binlog 合并成一条 DML 执行到上游,缩小网络的交互,如
INSERT tb(a,b) VALUES(1,1);
+ INSERT tb(a,b) VALUES(2,2);
= INSERT tb(a,b) VALUES(1,1),(2,2);
UPDATE tb SET a=1, b=1 WHERE a=1;
+ UPDATE tb SET a=2, b=2 WHERE a=2;
= INSERT tb(a,b) VALUES(1,1),(2,2) ON DUPLICATE UPDATE a=VALUES(a), b=VALUES(b)
DELETE tb WHERE a=1
+ DELETE tb WHERE a=2
= DELETE tb WHERE (a) IN (1),(2);
执行逻辑
DML 生成
DM 内嵌一个 schema tracker,用于记录上下游 schema 信息。当收到 DDL 时,DM 更新外部 schema tracker 的表构造。当收到 DML 时,DM 依据 schema tracker 的表构造生成对应的 DML,具体逻辑如下:
- 当启动全量加增量工作时,Sync 应用上游全量同步时 dump 进去的表构造作为上游的初始表构造
- 当启动增量工作时,因为 MySQL binlog 没有记录表构造信息,Sync 应用上游对应的表的表构造作为上游的初始表构造
- 因为用户上下游表构造可能不统一,如上游比上游多了额定的列,或者上下游主键不统一,为了保证数据同步的正确性,DM 记录上游对应表的主键和惟一键信息
- 生成 DML 时,DM 应用 schema tracker 中记录的上游表构造生成 DML 语句的列,应用 binlog 中记录的列值生成 DML 语句的列值,应用 schema tracker 中记录的上游主键 / 惟一键生成 DML 语句中的 WHERE 条件。当表构造无惟一键时,DM 会应用 binlog 中记录的所有列值作为 WHERE 条件。
Worker Count
上文中咱们晓得 Causality 能够通过冲突检测算法将 binlog 分成多个 group 并发地执行到上游,DM 通过设置 worker-count,管制并发的数量。当上游 TiDB 的 CPU 占用不高时,增大并发的数量能够无效的进步数据同步的吞吐量。通过 syncer.worker-count
配置
Batch
DM 将多条 DML 攒到一个事务中执行到上游,当 DML Worker 收到 DML 时,将其退出到缓存中,当缓存中 DML 数量达到预约阈值时,或者较长时间没有收到 DML 时,将缓存中的 DML 执行到上游。通过 syncer.batch
配置
Checkpoint
从下面的流程图中,咱们能够看到 DML 执行和 checkpoint 更新不是原子的。DM 中,checkpoint 默认每 30s 更新一次。同时,因为存在多个 DML worker 过程,checkpoint 过程计算所有 DML worker 同步进度最晚的 binlog 位点,将该位点作为以后同步的 checkpoint,所有早于此位点的 binlog,都已保障被胜利地执行到上游。
事务一致性
从下面的形容咱们能够看到,DM 实际上是依照“行级别”进行数据同步的,上游一个事务在 DM 中会被拆成多行,散发到不同的 DML Worker 中并发执行。当 DM 同步工作报错暂停,或者用户手动暂停工作时,上游可能停留在一个中间状态,即上游一个事务中的 DML 语句,可能一部分同步到上游,一部分没有,上游处于一个不统一的状态。为了尽可能使工作暂停时,上游处于统一状态,DM 在 v5.3.0 后,在工作暂停时会期待上游事务全副同步到上游后,才真正暂停工作,这个等待时间为 10s,如果上游一个事务在 10s 内还未全副同步到上游,那么上游依然可能处于不统一的状态。
Safemode
在下面的执行逻辑章节,咱们能够发现 DML 执行 和写 checkpoint 操作并不是同步的,并且写 checkpoint 操作和写上游数据也并不能保障原子性,当 DM 因为某些起因异样退出时,checkpoint 可能只记录到退出时刻之前的一个复原点,因而当同步工作重启时,DM 可能会反复写入局部数据,也就是说,DM 实际上提供的是“至多一次解决”的逻辑(At-least-once processing),雷同的数据可能会被解决一次以上。为了保证数据是可重入的,DM 在异样重启时会进入 safemode 模式。具体逻辑如下:
1. 当 DM 工作失常暂停时,会将内存中所有的 DML 全副同步到上游,并刷新 checkpoint。工作失常暂停后重启不会进入 safemode,因为 checkpoint 之前的数据全副都被同步到上游,checkpoint 之后的数据还未同步过,没有数据会被反复解决
2. 当工作异样暂停时,DM 会先尝试将内存中所有的 DML 全副同步到上游,此时可能会失败(如上游数据抵触等),而后,DM 记录以后内存中从上游拉取到的最新的 binlog 的位点,记作 safemode\_exit\_point,将这个位点和 checkpoint 一起刷新到上游。当工作复原后,可能存在以下情景
- checkpoint == safemode\_exit\_point,这意味着 DM 暂停时所有的 DML 全副同步到上游,咱们能够依照工作失常暂停时的解决办法,不必进入 safemode
- checkpoint < safemode\_exit\_point,这意味着 DM 暂停时,内存中的局部 DML 执行到上游时失败,所以 checkpoint 仍是一个较“老”的位点,这时,从 checkpoint 到 safemode\_exit\_point 这一段 binlog,都会开启 safemode 模式,因为它们可能曾经被同步过一次了
- safemode\_exit\_point 不存在,这意味着 DM 暂停时刷新 safemode\_exit\_point 的操作失败了,或者 DM 过程被强制完结了。此时 DM 无奈具体判断哪些数据可能被反复解决,因而会在工作复原后的两个 checkpoint 距离中(默认为一分钟),开启 safemode,之后会敞开 safemode 失常同步
Safemode 期间,为了保证数据可重入,DM 会进行如下转换
- 将上游 insert 语句,转换成 replace 语句
- 将上游 update 语句,转换成 delete + replace 语句
准确一次解决(Exactly-Once Processing)
从下面的形容,咱们能够发现 DM 这种拆事务而后并发同步的逻辑引发了一些问题,比方上游可能停在一个不统一的状态,比方数据的同步程序与上游不统一,比方可能导致数据重入(safemode 期间 replace 语句会有肯定的性能损失,如果上游须要捕捉数据变更(如 cdc),那么反复解决也不可承受)。
综上,咱们正在思考实现“准确一次解决”的逻辑,如果有趣味退出咱们的,能够来到 https://internals.tidb.io,一起探讨。