DM 源码阅读系列文章(四)dump/load 全量同步的实现丨TiDB 工具

杨非 产品技术解读 2019-04-26

本文为 TiDB Data Migration 源码阅读系列文章的第四篇,《DM 源码阅读系列文章(三)数据同步处理单元介绍》介绍了数据同步处理单元实现的功能,数据同步流程的运行逻辑以及数据同步处理单元的 interface 设计。本篇文章在此基础上展开,详细介绍 dump 和 load 两个数据同步处理单元的设计实现,重点关注数据同步处理单元 interface 的实现,数据导入并发模型的设计,以及导入任务在暂停或出现异常后如何恢复。

dump 处理单元

dump 处理单元的代码位于github.com/pingcap/dm/mydumper包内,作用是从上游 MySQL 将表结构和数据导出到逻辑 SQL 文件,由于该处理单元总是运行在任务的第一个阶段(full 模式和 all 模式),该处理单元每次运行不依赖于其他处理单元的处理结果。另一方面,如果在 dump 运行过程中被强制终止(例如在 dmctl 中执行 pause-task 或者 stop-task),也不会记录已经 dump 数据的 checkpoint 等信息。不记录 checkpoint 是因为每次运行 Mydumper 从上游导出数据,上游的数据都可能发生变更,为了能得到一致的数据和 metadata 信息,每次恢复任务或重新运行任务时该处理单元会清理旧的数据目录,重新开始一次完整的数据 dump。

导出表结构和数据的逻辑并不是在 DM 内部直接实现,而是通过os/exec包调用外部 mydumper 二进制文件来完成。在 Mydumper 内部,我们需要关注以下几个问题:

  • 数据导出时的并发模型是如何实现的。

  • no-locks, lock-all-tables, less-locking 等参数有怎样的功能。

  • 库表黑白名单的实现方式。

Mydumper 的实现细节

Mydumper的一次完整的运行流程从主线程开始,主线程按照以下步骤执行:

  1. 解析参数。

  2. 创建到数据库的连接

  3. 会根据no-locks选项进行一系列的备份安全策略,包括long query guardlock all tables or FLUSH TABLES WITH READ LOCK

  4. START TRANSACTION WITH CONSISTENT SNAPSHOT

  5. 记录 binlog 位点信息

  6. less locking处理线程的初始化

  7. 普通导出线程初始化

  8. 如果配置了trx-consistency-only选项,执行UNLOCK TABLES /* trx-only */释放之前获取的表锁。注意,如果开启该选项,是无法保证非 InnoDB 表导出数据的一致性。更多关于一致性读的细节可以参考MySQL 官方文档 Consistent Nonlocking Reads 部分

  9. 根据配置规则(包括 --database, --tables-list 和 --regex 配置)读取需要导出的 schema 和表信息,并在这个过程中有区分的记录 innodb_tables 和 non_innodb_table

  10. 为工作子线程创建任务,并将任务 push 到相关的工作队列

  11. 如果没有配置no-lockstrx-consistency-only选项,执行 UNLOCK TABLES /* FTWRL */ 释放锁

  12. 如果开启less-locking,等待所有less locking子线程退出

  13. 等待所有工作子线程退出

工作线程的并发控制包括了两个层面,一层是在不同表级别的并发,另一层是同一张表级别的并发。Mydumper 的主线程会将一次同步任务拆分为多个同步子任务,并将每个子任务分发给同一个异步队列conf.queue_less_locking/conf.queue,工作子线程从队列中获取任务并执行。具体的子任务划分包括以下策略:

从上述的并发模型可以看出 Mydumper 首先按照表进行同步任务拆分,对于同一张表,如果配置rows-per-file参数,会根据该参数和表行数将表划分为合适的chunks数,这即是同一张表内部的并发。具体表行数的估算和chunks划分的实现见get_chunks_for_table函数。

需要注意目前 DM 在任务配置中指定的库表黑白名单功能只应用于 load 和 binlog replication 处理单元。如果在 dump 处理单元内使用库表黑白名单功能,需要在同步任务配置文件的 dump 处理单元配置提供 extra-args 参数,并指定 Mydumper 相关参数,包括 --database, --tables-list 和 --regex。Mydumper 使用 regex 过滤库表的实现参考check_regex函数。

load 处理单元

load 处理单元的代码位于github.com/pingcap/dm/loader包内,该处理单元在 dump 处理单元运行结束后运行,读取 dump 处理单元导出的 SQL 文件解析并在下游数据库执行逻辑 SQL。我们重点分析InitProcess两个 interface 的实现。

Init 实现细节

该阶段进行一些初始化和清理操作,并不会开始同步任务,如果在该阶段运行中出现错误,会通过rollback 机制清理资源,不需要调用 Close 函数。该阶段包含的初始化操作包括以下几点:

Process 实现细节

该阶段的工作流程也很直观,通过一个收发数据类型为*pb.ProcessErrorchannel接收运行过程中出现的错误,出错后通过 context 的CancelFunc强制结束处理单元运行。在核心的数据导入函数中,工作模型与 mydumper 类似,即在主线程中分发任务有多个工作线程执行具体的数据导入任务。具体的工作细节如下: