DM 增量数据校验

本文介绍了如何使用 DM 增量数据校验功能、DM 增量数据校验的原理以及相关的使用限制。

使用场景

在将增量数据从上游迁移到下游数据库的过程中,数据的流转有小概率导致错误或者丢失的情况。对于需要依赖于强数据一致的场景,如信贷、证券等业务,你可以在数据迁移完成之后对数据进行全量校验,确保数据的一致性。然而,在某些增量复制的业务场景下,上游和下游的写入是持续的、不会中断的,因为上下游的数据在不断变化,导致用户难以对表里面的全部数据进行一致性校验(例如使用 sync-diff-inspector)。

在增量数据复制的场景下,你可以使用 DM 的增量校验功能,在数据持续写入下游的增量复制过程中确保迁移数据的完整性、一致性

开启增量数据校验

你可以使用下面任意方法开启增量数据校验:

  • 在任务配置中开启
  • 通过 dmctl 开启

方法 1:在任务配置中开启

你可以在任务配置文件中加入以下内容来开启增量数据校验:

  1. # 给需要开启增量校验功能的上游数据库添加增量校验配置
  2. mysql-instances:
  3. - source-id: "mysql1"
  4. block-allow-list: "bw-rule-1"
  5. validator-config-name: "global"
  6. validators:
  7. global:
  8. mode: full # 也可以是 fast,默认是 none,即不开启校验
  9. worker-count: 4 # 后台校验的 validation worker 数量,默认是 4 个
  10. row-error-delay: 30m # 某一行多久没有验证通过会被标记为 error row,默认是 30m,即 30 分钟

示例中各配置项的含义如下:

  • mode:校验模式,可以是 nonefullfast
    • none:默认值,即不开启校验。
    • full:将变动行和下游数据库中获取的行数据进行每列对比。
    • fast:只判断这一行在下游数据库是否存在。
  • worker-count:增量校验功能使用的 worker 数量(每个 worker 都是一个 goroutine)。
  • row-error-delay:某一行多久没有验证通过会被标记为 error row,默认是 30 分钟。

完整配置请查阅 DM 任务完整配置文件

方法 2:通过 dmctl 开启

你可以使用 dmctl validation start 命令来开启增量数据校验:

  1. Usage:
  2. dmctl validation start [--all-task] [task-name] [flags]
  3. Flags:
  4. --all-task whether applied to all tasks
  5. -h, --help help for start
  6. --mode string specify the mode of validation: full (default), fast; this flag will be ignored if the validation task has been ever enabled but currently paused (default "full")
  7. --start-time string specify the start time of binlog for validation, e.g. '2021-10-21 00:01:00' or 2021-10-21T00:01:00
  • --mode:指定开启的模式,可以是 fast 或者 full。
  • --start-time:指定 validator 开启校验的位置,格式是:2021-10-21 00:01:00 或者 2021-10-21T00:01:00。
  • task-name:需要开启增量数据校验的任务名,你也可以用 --all-task 来为当前所有任务开启增量数据校验。

示例:

  1. dmctl --master-addr=127.0.0.1:8261 validation start --start-time 2021-10-21T00:01:00 --mode full my_dm_task

使用增量数据校验

在使用增量数据校验时,通过 dmctl 工具,你可以查询到增量校验当前的校验状态,也可以对校验出的错误行(error row)进行及时处理。所谓的错误行,就是在增量校验过程中,被检查出上下游数据不一致的行。

查看增量校验的状态

你可以使用两种方式查看增量校验的状态。

方式 1:用 dmctl query-status <task-name> 命令查看任务状态,如果开启了增量校验,校验结果会显示在每个 subtask 的 validation 字段里面。示例输出:

  1. "subTaskStatus": [
  2. {
  3. "name": "test",
  4. "stage": "Running",
  5. "unit": "Sync",
  6. "result": null,
  7. "unresolvedDDLLockID": "",
  8. "sync": {
  9. ...
  10. },
  11. "validation": {
  12. "task": "test", // 任务名
  13. "source": "mysql-01", // source id
  14. "mode": "full", // 校验模式
  15. "stage": "Running", // 当前状态,Running 或者 Stopped
  16. "validatorBinlog": "(mysql-bin.000001, 5989)", // 校验到的 binlog 位置
  17. "validatorBinlogGtid": "1642618e-cf65-11ec-9e3d-0242ac110002:1-30", // 同上,用 GTID 表示
  18. "result": null, // 当增量校验异常时,显示异常信息
  19. "processedRowsStatus": "insert/update/delete: 0/0/0", // 已经处理的 binlog 数据行的统计信息
  20. "pendingRowsStatus": "insert/update/delete: 0/0/0", // 还未校验或者校验失败,但还没标记为`错误行`的数据行统计信息
  21. "errorRowsStatus": "new/ignored/resolved: 0/0/0" // `错误行`统计信息,三种状态的错误会在下文讲解
  22. }
  23. }
  24. ]

方式 2:使用 dmctl validation status <taskname> 来查询增量校验的状态:

  1. dmctl validation status [--table-stage stage] <task-name> [flags]
  2. Flags:
  3. -h, --help help for status
  4. --table-stage string filter validation tables by stage: running/stopped

在上述命令中,你可以设置 --table-stage 来过滤正在校验或者已经停止校验的表。示例输出:

  1. {
  2. "result": true,
  3. "msg": "",
  4. "validators": [
  5. {
  6. "task": "test",
  7. "source": "mysql-01",
  8. "mode": "full",
  9. "stage": "Running",
  10. "validatorBinlog": "(mysql-bin.000001, 6571)",
  11. "validatorBinlogGtid": "",
  12. "result": null,
  13. "processedRowsStatus": "insert/update/delete: 2/0/0",
  14. "pendingRowsStatus": "insert/update/delete: 0/0/0",
  15. "errorRowsStatus": "new/ignored/resolved: 0/0/0"
  16. }
  17. ],
  18. "tableStatuses": [
  19. {
  20. "source": "mysql-01", // source id
  21. "srcTable": "`db`.`test1`", // 源表名
  22. "dstTable": "`db`.`test1`", // 目标表名
  23. "stage": "Running", // 校验状态
  24. "message": "" // 具体错误信息显示
  25. }
  26. ]
  27. }

如果你想要查询错误行的详细信息,比如错误原因、错误时间等,可以使用 dmctl validation show-error 命令:

  1. Usage:
  2. dmctl validation show-error [--error error-state] <task-name> [flags]
  3. Flags:
  4. --error string filtering type of error: all, ignored, or unprocessed (default "unprocessed")
  5. -h, --help help for show-error

示例输出:

  1. {
  2. "result": true,
  3. "msg": "",
  4. "error": [
  5. {
  6. "id": "1", // 错误行标识符,在后续的处理错误行中用到
  7. "source": "mysql-replica-01", // source id
  8. "srcTable": "`validator_basic`.`test`", // 错误行源表
  9. "srcData": "[0, 0]", // 错误行具体数据
  10. "dstTable": "`validator_basic`.`test`", // 错误行目标表
  11. "dstData": "[]", // 错误行在下游的数据
  12. "errorType": "Expected rows not exist", // 错误原因
  13. "status": "NewErr", // 错误状态
  14. "time": "2022-07-04 13:33:02", // 错误行发现时间
  15. "message": "" // 额外信息
  16. }
  17. ]
  18. }

处理增量校验错误行

当增量数据校验发现错误行后,你需要手动处理这些错误行。

在增量校验出现错误行时,增量校验不会停下,而是会把这些错误行记录下来,让用户自己去发现处理。错误行没有被处理时,默认状态是 unprocessed。如果你在下游手动矫正了该错误行的错误,增量校验也不会去自动获取矫正后的信息,仍会将该错误行记录在 error 中。

如果你不想在 validation status 中再看到这个错误行、或者你需要给已经解决的错误行打上标记,你可以使用 validation show-error 找到错误行的 id,然后使用错误处理命令来对这些错误行进行处理或者标记。

dmctl 提供了三种错误处理命令:

  • clear-error:清理掉错误行,show-error命令将不再展示该error row

    1. Usage:
    2. dmctl validation clear-error <task-name> <error-id|--all> [flags]
    3. Flags:
    4. --all all errors
    5. -h, --help help for clear-error
  • ignore-error:忽略该错误行,将这个错误行标记为 ignored。

    1. Usage:
    2. dmctl validation ignore-error <task-name> <error-id|--all> [flags]
    3. Flags:
    4. --all all errors
    5. -h, --help help for ignore-error
  • resolve-error:已手动解决该错误行,将这个错误行标记为 resolved。

    1. Usage:
    2. dmctl validation resolve-error <task-name> <error-id|--all> [flags]
    3. Flags:
    4. --all all errors
    5. -h, --help help for resolve-error

停止增量数据校验

如果你需要停止增量数据校验,可以使用 validation stop 命令:

  1. Usage:
  2. dmctl validation stop [--all-task] [task-name] [flags]
  3. Flags:
  4. --all-task whether applied to all tasks
  5. -h, --help help for stop

用法可参考 dmctl validation start 命令

原理

DM 增量校验(validator)的简要架构如下所示:

validator summary

增量数据校验的工作生命周期如下所示:

validator lifecycle

增量数据校验的具体处理流程如下:

  1. validator 从上游拉取 binlog 事件,获取到发生变更的数据行:
    • validator 只会校验增量复制 (syncer) 完成的事件,如果该事件还没有被 syncer 处理,则 validator 会暂停,等待 syncer 处理完成。
    • 如果该事件已被 syncer 处理完成,则进行下面的步骤。
  2. validator 将 binlog 解析,并通过黑白名单、过滤器的筛选,表路由的重定向(和 syncer 保持一致)之后,将这些行变动交给在后台运行的 validation worker。
  3. validation worker 合并相同表、相同主键的行变动,避免进行“过期”的校验,并将这些行先缓存到内存中。
  4. 当 validation worker 积攒了一定数量的行或者到了某个时间间隔之后,validation worker 根据这些行的主键信息在下游数据库中查询当下的数据,并和变动行期望的数据进行对比。
  5. validation worker 进行数据校验。如果当前配置是 full mode,会将变动行和下游数据库中获取的行数据进行每列对比;如果当前配置是 fast mode,则只会判断这一行是否还存在。
    • 如果校验成功,则将该行从内存中删除。
    • 如果校验失败,不会马上报错,而是在一定间隔之后继续校验。
    • 对于某些已经在很长时间(由用户定义)内都没有校验成功的行,则将这些行定义为错误行(error row),写入到下游的 meta 库中。你可以通过查询迁移任务信息来获取错误行的数量等信息,详见查看增量校验的状态以及处理增量校验错误行

使用限制

  • 校验目标表必须有主键或者 not null 的唯一键。
  • 上游迁移 DDL 时有以下限制:
    • DDL 不能变更主键,不能调整列顺序,不能删除已有列。
    • 该表不能被 DROP。
  • 不支持按照 expression 过滤事件的任务。
  • 由于 TiDB 和 MySQL 的浮点数精度有差异,精度范围内误差也会判断为相等(即绝对误差小于 10^-6)。
  • 不支持校验的数据类型:
    • JSON
    • 二进制数据