DM 增量数据校验

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

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

    开启增量数据校验

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

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

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

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

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

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

    你可以使用 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. "pendingRowsStatus": "insert/update/delete: 0/0/0", // 还未校验或者校验失败,但还没标记为`错误行`的数据行统计信息
    19. "errorRowsStatus": "new/ignored/resolved: 0/0/0" // `错误行`统计信息,三种状态的错误会在下文讲解
    20. }
    21. }
    22. ]

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

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

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

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

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

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

    • 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. -h, --help help for stop

    用法可参考 dmctl validation start 命令

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

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

    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)。
    • 不支持校验的数据类型:
      • 二进制数据