数据迁移功能
本文将详细介绍 DM 提供的数据迁移功能,以及相关的配置选项。
Table Routing、Block & Allow Lists、Binlog Event Filter 在匹配库表名时,有以下版本差异:
- 对于 v1.0.5 版及后续版本,以上功能均支持通配符匹配。但注意所有版本中通配符匹配中的
*符号 只能有一个且必须在末尾。 - 对于 v1.0.5 以前的版本,Table Routing 和 Binlog Event Filter 支持通配符,但不支持
[...]与[!...]表达式。Block & Allow Lists 仅支持正则表达式。
在简单任务场景下推荐使用通配符匹配。
Table routing
Table routing 提供将上游 MySQL/MariaDB 实例的某些表迁移到下游指定表的功能。
参数配置
routes:
rule-1:
schema-pattern: "test_*"
table-pattern: "t_*"
target-schema: "test"
target-table: "t"
rule-2:
schema-pattern: "test_*"
target-schema: "test"
参数解释
将根据 schema-pattern/table-pattern 匹配上该规则的上游 MySQL/MariaDB 实例的表迁移到下游的 target-schema/target-table。
使用示例
下面展示了三个不同场景下的配置示例。
分库分表合并
假设存在分库分表场景,需要将上游两个 MySQL 实例的表 test_{1,2,3...}.t_{1,2,3...} 迁移到下游 TiDB 的一张表 test.t。
为了迁移到下游实例的表 test.t 需要创建两个 table routing 规则:
rule-1用来迁移匹配上schema-pattern: "test_*"和table-pattern: "t_*"的表的 DML/DDL 语句到下游的test.t。rule-2用来迁移匹配上schema-pattern: "test_*"的库的 DDL 语句,例如create/drop schema xx。
rule-1:
schema-pattern: "test_*"
table-pattern: "t_*"
target-schema: "test"
target-table: "t"
rule-2:
schema-pattern: "test_*"
target-schema: "test"
分库合并
假设存在分库场景,将上游两个 MySQL 实例 test_{1,2,3...}.t_{1,2,3...} 迁移到下游 TiDB 的 test.t_{1,2,3...},创建一条路由规则即可:
rule-1:
schema-pattern: "test_*"
target-schema: "test"
错误的 table routing
假设存在下面两个路由规则,test_1_bak.t_1_bak 可以匹配上 rule-1 和 rule-2,违反 table 路由的限制而报错。
rule-0:
schema-pattern: "test_*"
target-schema: "test"
rule-1:
schema-pattern: "test_*"
table-pattern: "t_*"
target-schema: "test"
target-table: "t"
rule-2:
schema-pattern: "test_1_bak"
table-pattern: "t_1_bak"
target-schema: "test"
target-table: "t_bak"
Block & allow table lists
上游数据库实例表的黑白名单过滤规则,可以用来过滤或者只迁移某些 database/table 的所有操作。
参数配置
block-allow-list: # 如果 DM 版本 <= v1.0.6 则使用 black-white-list
rule-1:
do-dbs: ["test*"] # 非 ~ 字符开头,表示规则是通配符;v1.0.5 及后续版本支持通配符规则。
do-tables:
- db-name: "test[123]" # 匹配 test1、test2、test3。
tbl-name: "t[1-5]" # 匹配 t1、t2、t3、t4、t5。
- db-name: "test"
tbl-name: "t"
rule-2:
do-dbs: ["~^test.*"] # 以 ~ 字符开头,表示规则是正则表达式。
ignore-dbs: ["mysql"]
do-tables:
- db-name: "~^test.*"
tbl-name: "~^t.*"
- db-name: "test"
tbl-name: "t"
ignore-tables:
- db-name: "test"
tbl-name: "log"
参数解释
do-dbs:要迁移的库的白名单,类似于 MySQL 中的replicate-do-db。ignore-dbs:要迁移的库的黑名单,类似于 MySQL 中的replicate-ignore-db。do-tables:要迁移的表的白名单,类似于 MySQL 中的replicate-do-table。ignore-tables:要迁移的表的黑名单,类似于 MySQL 中的replicate-ignore-table。
以上参数值以 ~ 开头时均支持使用正则表达式来匹配库名、表名。
过滤规则
do-dbs 与 ignore-dbs 对应的过滤规则与 MySQL 中的 Evaluation of Database-Level Replication and Binary Logging Options 类似,do-tables 与 ignore-tables 对应的过滤规则与 MySQL 中的 Evaluation of Table-Level Replication Options 类似。
判断 table test.t 是否应该被过滤的过滤流程如下:
首先 schema 过滤判断
如果
do-dbs不为空,判断do-dbs中是否存在一个匹配的 schema。- 如果存在,则进入 table 过滤判断。
- 如果不存在,则过滤
test.t。
如果
do-dbs为空并且ignore-dbs不为空,判断ignore-dbs中是否存在一个匹配的 schema。- 如果存在,则过滤
test.t。 - 如果不存在,则进入 table 过滤判断。
- 如果存在,则过滤
如果
do-dbs和ignore-dbs都为空,则进入 table 过滤判断。
进行 table 过滤判断
如果
do-tables不为空,判断do-tables中是否存在一个匹配的 table。- 如果存在,则迁移
test.t。 - 如果不存在,则过滤
test.t。
- 如果存在,则迁移
如果
ignore-tables不为空,判断ignore-tables中是否存在一个匹配的 table。- 如果存在,则过滤
test.t. - 如果不存在,则迁移
test.t。
- 如果存在,则过滤
如果
do-tables和ignore-tables都为空,则迁移test.t。
使用示例
假设上游 MySQL 实例包含以下表:
`logs`.`messages_2016`
`logs`.`messages_2017`
`logs`.`messages_2018`
`forum`.`users`
`forum`.`messages`
`forum_backup_2016`.`messages`
`forum_backup_2017`.`messages`
`forum_backup_2018`.`messages`
配置如下:
block-allow-list: # 如果 DM 版本 <= v1.0.6 则使用 black-white-list
bw-rule:
do-dbs: ["forum_backup_2018", "forum"]
ignore-dbs: ["~^forum_backup_"]
do-tables:
- db-name: "logs"
tbl-name: "~_2018$"
- db-name: "~^forum.*"
tbl-name: "messages"
ignore-tables:
- db-name: "~.*"
tbl-name: "^messages.*"
应用 bw-rule 规则后:
Binlog event filter
Binlog event filter 是比迁移表黑白名单更加细粒度的过滤规则,可以指定只迁移或者过滤掉某些 schema / table 的指定类型 binlog,比如 INSERT,TRUNCATE TABLE。
参数配置
filters:
rule-1:
schema-pattern: "test_*"
table-pattern: "t_*"
events: ["truncate table", "drop table"]
sql-pattern: ["^DROP\\s+PROCEDURE", "^CREATE\\s+PROCEDURE"]
action: Ignore
参数解释
schema-pattern/table-pattern:对匹配上的上游 MySQL/MariaDB 实例的表的 binlog events 或者 DDL SQL 语句进行以下规则过滤。events:binlog events 数组。sql-pattern:用于过滤指定的 DDL SQL 语句,支持正则表达式匹配,例如上面示例"^DROP\\s+PROCEDURE"。action:string(Do/Ignore);进行下面规则判断,满足其中之一则过滤,否则不过滤。Do:白名单。binlog event 如果满足下面两个条件之一就会被过滤掉:- 不在该 rule 的
events中。 - 如果规则的
sql-pattern不为空的话,对应的 SQL 没有匹配上sql-pattern中任意一项。
- 不在该 rule 的
Ignore:黑名单。如果满足下面两个条件之一就会被过滤掉:- 在该 rule 的
events中。 - 如果规则的
sql-pattern不为空的话,对应的 SQL 可以匹配上sql-pattern中任意一项。
- 在该 rule 的
使用示例
过滤分库分表的所有删除操作
需要设置下面两个 Binlog event filter rule 来过滤掉所有的删除操作:
filter-table-rule过滤掉所有匹配到 patterntest_*.t_*的 table 的turncate table、drop table、delete statement操作。filter-schema-rule过滤掉所有匹配到 patterntest_*的 schema 的drop database操作。
filters:
filter-table-rule:
schema-pattern: "test_*"
table-pattern: "t_*"
events: ["truncate table", "drop table", "delete"]
action: Ignore
filter-schema-rule:
schema-pattern: "test_*"
events: ["drop database"]
action: Ignore
只迁移分库分表的 DML 操作
需要设置下面两个 Binlog event filter rule 只迁移 DML 操作:
do-table-rule只迁移所有匹配到 patterntest_*.t_*的 table 的create table、insert、update、delete操作。do-schema-rule只迁移所有匹配到 patterntest_*的 schema 的create database操作。
filters:
do-table-rule:
schema-pattern: "test_*"
table-pattern: "t_*"
events: ["create table", "all dml"]
action: Do
do-schema-rule:
schema-pattern: "test_*"
events: ["create database"]
action: Do
过滤 TiDB 不支持的 SQL 语句
可设置如下规则过滤 TiDB 不支持的 PROCEDURE 语句:
filters:
filter-procedure-rule:
schema-pattern: "test_*"
table-pattern: "t_*"
sql-pattern: ["^DROP\\s+PROCEDURE", "^CREATE\\s+PROCEDURE"]
action: Ignore
过滤 TiDB parser 不支持的 SQL 语句
对于 TiDB parser 不支持的 SQL 语句,DM 无法解析获得 schema/table 信息,因此需要使用全局过滤规则:schema-pattern: "*"。
可设置如下规则过滤 TiDB parser 不支持的 PARTITION 语句:
filters:
filter-partition-rule:
schema-pattern: "*"
sql-pattern: ["ALTER\\s+TABLE[\\s\\S]*ADD\\s+PARTITION", "ALTER\\s+TABLE[\\s\\S]*DROP\\s+PARTITION"]
action: Ignore
Column mapping
Column mapping 提供对表的列值进行修改的功能。可以根据不同的表达式对表的指定列做不同的修改操作,目前只支持 DM 提供的内置表达式。
参数配置
column-mappings:
rule-1:
schema-pattern: "test_*"
table-pattern: "t_*"
expression: "partition id"
source-column: "id"
target-column: "id"
arguments: ["1", "test", "t", "_"]
rule-2:
schema-pattern: "test_*"
table-pattern: "t_*"
expression: "partition id"
source-column: "id"
target-column: "id"
arguments: ["2", "test", "t", "_"]
参数解释
schema-pattern/table-pattern:对匹配上该规则的上游 MySQL/MariaDB 实例的表按照指定expression进行列值修改操作。source-column,target-column:对source-column列的值按照指定expression进行修改,将修改后的值赋值给target-column。expression:对数据进行转换的表达式,目前只支持下面的内置计算表达式。
partition id 表达式
partition id 目的是为了解决分库分表合并迁移的自增主键的冲突。
partition id 限制
注意下面的限制:
- 只支持类型为 bigint 的列,通常为自增主键,联合主键或者联合唯一索引的其中一列
- 如果
schema 前缀不为空,则库名的组成必须为schema 前缀或者schema 前缀 + 分隔符 + 数字(即 schema ID),例如:支持s和s_1,不支持s_a - 如果
table 前缀不为空,则表名的组成必须为table 前缀或者table 前缀 + 分隔符 + 数字(即 table ID) - 如果库名/表名不包含
… + 分隔符 + 数字部分,则对应的 ID 默认为 0 - 对分库分表的规模支持限制如下
- 支持最多 16 个 MySQL/MariaDB 实例,且需要满足 0 <= instance ID <= 15。
- 每个实例支持最多 128 个 schema,且需要满足 0 <= schema ID <= 127。
- 每个实例的每个 schema 支持最多 256 个 table,且需要满足 0 <= table ID <= 255。
- 进行 Column mapping 的列的范围需要满足 0 <= ID <= 17592186044415。
{instance ID, schema ID, table ID}组合需要保持唯一。
partition id 参数配置
用户需要在 arguments 里面按顺序设置以下三个或四个参数:
instance_id:客户指定的上游分库分表的 MySQL/MariaDB instance ID(0 <= instance ID <= 15)schema 前缀:用来解析库名并获取schema IDtable 前缀:用来解释表名并获取table ID- 分隔符:用来分隔前缀与 ID,可省略,省略时分隔符默认为空字符串
instance_id、schema 前缀 和 table 前缀 这三个参数均可被设置为空字符串(""),表示对应的部分不会被编码进 partition id。
partition id 表达式规则
partition id 会用 arguments 里面的数字来填充自增主键 ID 的首个比特位,计算出来一个 int64(即 MySQL bigint)类型的值,具体规则如下:
S:符号位,保留I:instance ID,默认 4 比特位D:schema ID,默认 7 比特位T:table ID,默认 8 比特位P:自增主键 ID,占据剩下的比特位(≥44 比特位)
使用示例
假设存在分库分表场景:将上游两个 MySQL 实例的 test_{1,2,3...}.t_{1,2,3...} 迁移到下游 TiDB 的 test.t,并且这些表都有自增主键。
需要设置下面两个规则:
column-mappings:
rule-1:
schema-pattern: "test_*"
table-pattern: "t_*"
expression: "partition id"
source-column: "id"
target-column: "id"
arguments: ["1", "test", "t", "_"]
rule-2:
schema-pattern: "test_*"
table-pattern: "t_*"
expression: "partition id"
source-column: "id"
target-column: "id"
arguments: ["2", "test", "t", "_"]
- MySQL instance 1 的表
test_1.t_1的ID = 1的行经过转换后 ID = 1 变为1 << (64-1-4) | 1 << (64-1-4-7) | 1 << 44 | 1 = 580981944116838401 - MySQL instance 2 的表
test_1.t_2的ID = 1的行经过转换后 ID = 2 变为2 << (64-1-4) | 1 << (64-1-4-7) | 2 << 44 | 2 = 1157460288606306306
迁移延迟监控
DM 支持通过 heartbeat 真实迁移数据来计算每个迁移任务与 MySQL/MariaDB 的实时迁移延迟。
系统权限
如果开启 heartbeat 功能,需要上游 MySQL/MariaDB 实例提供下面的权限:
- SELECT
- INSERT
- CREATE (databases, tables)
- DELETE
参数配置
在 task 的配置文件中设置:
enable-heartbeat: true
原理介绍
- DM-worker 在对应的上游 MySQL/MariaDB 创建库
dm_heartbeat(当前不可配置) - DM-worker 在对应的上游 MySQL/MariaDB 创建表
heartbeat(当前不可配置) - DM-worker 每秒钟(当前不可配置)在对应的上游 MySQL/MariaDB 的
dm_heartbeat.heartbeat表中,利用replace statement更新当前时间戳TS_master - DM-worker 每个任务拿到
dm_heartbeat.heartbeat的 binlog 后,更新自己的迁移时间TS_slave_task - DM-worker 每 10 秒在对应的上游 MySQL/MariaDB 的
dm_heartbeat.heartbeat查询当前的TS_master,并且对每个任务计算task_lag=TS_master-TS_slave_task
可以在 metrics 的 binlog replication 处理单元找到 replicate lag 监控项。