分表合并迁移到 TiDB

本文介绍如何在分库分表合并场景中使用 Data Migration (DM) 将上游数据迁移至下游 TiDB 集群。

下面介绍了一个简单的场景,两个数据源 MySQL 实例的分库和分表数据需要迁移至下游 TiDB 集群。更多详情请参阅分表合并数据迁移最佳实践

数据源实例

假设数据源结构如下:

  • 实例 1

    SchemaTables
    userinformation, log_bak
    store_01sale_01, sale_02
    store_02sale_01, sale_02
  • 实例 2

    SchemaTables
    userinformation, log_bak
    store_01sale_01, sale_02
    store_02sale_01, sale_02

迁移需求

  1. user.information 需要合并到下游 TiDB 中的 user.information 表。
  2. 实例中的 store_{01|02}.sale_{01|02} 表合并至下游 TiDB 中的 store.sale 表。
  3. 同步 userstore_{01|02} 库,但不同步两个实例的 user.log_bak 表。
  4. 过滤掉两个实例中 store_{01|02}.sale_{01|02} 表的所有删除操作,并过滤该库的 drop database 操作。

预期迁移后下游库结构如下:

SchemaTables
userinformation
storesale

分表数据冲突检查

迁移需求 #1 和 #2 涉及合库合表,来自多张分表的数据可能引发主键或唯一索引的数据冲突。这需要我们检查这几组分表数据的业务特点,详情请见跨分表数据在主键或唯一索引冲突处理。在本示例中:

user.information 表结构为

CREATE TABLE `information` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `uid` bigint(20) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `data` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

其中 id 列为主键,uid 列为唯一索引。id 列具有自增属性,多个分表范围重复会引发数据冲突。 uid 可以保证全局满足唯一索引,因此可以按照参考去掉自增主键的主键属性中介绍的操作绕过 id 列。

store_{01|02}.sale_{01|02} 的表结构为

CREATE TABLE `sale_01` (
  `sid` bigint(20) NOT NULL,
  `pid` bigint(20) NOT NULL,
  `comment` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`sid`),
  KEY `pid` (`pid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

其中 sid 是分片键,可以保证同一个 sid 只会划分到一个分表中,因此不会引发数据冲突,无需进行额外操作。

迁移方案

  • 要满足迁移需求 #1,无需配置 table routing 规则。按照去掉自增主键的主键属性的要求,在下游手动建表。

    CREATE TABLE `information` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `uid` bigint(20) DEFAULT NULL,
      `name` varchar(255) DEFAULT NULL,
      `data` varchar(255) DEFAULT NULL,
      INDEX (`id`),
      UNIQUE KEY `uid` (`uid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1

    并在配置文件中跳过前置检查

    ignore-checking-items: ["auto_increment_ID"]
  • 要满足迁移需求 #2,配置 table routing 规则如下:

    routes:
      ...
      store-route-rule:
        schema-pattern: "store_*"
        target-schema: "store"
      sale-route-rule:
        schema-pattern: "store_*"
        table-pattern: "sale_*"
        target-schema: "store"
        target-table:  "sale"
  • 要满足迁移需求 #3,配置 Block & Allow Lists 如下:

    block-allow-list:
      log-bak-ignored:
        do-dbs: ["user", "store_*"]
        ignore-tables:
        - db-name: "user"
          tbl-name: "log_bak"
  • 要满足迁移需求 #4,配置 Binlog event filter 规则如下:

    filters:
      ...
      sale-filter-rule:     # 过滤掉 store_* 库下面任何表的任何删除操作
        schema-pattern: "store_*"
        table-pattern: "sale_*"
        events: ["truncate table", "drop table", "delete"]
        action: Ignore
      store-filter-rule:   # 过滤掉删除 store_* 库的操作
        schema-pattern: "store_*"
        events: ["drop database"]
        action: Ignore

迁移任务配置

迁移任务的完整配置如下,更多详情请参阅数据迁移任务配置向导

name: "shard_merge"
task-mode: all                                   # 进行全量数据迁移 + 增量数据迁移
meta-schema: "dm_meta"
ignore-checking-items: ["auto_increment_ID"]

target-database:
  host: "192.168.0.1"
  port: 4000
  user: "root"
  password: ""

mysql-instances:
  -
    source-id: "instance-1"        # 数据源 ID,可以从数据源配置中获取
    route-rules: ["store-route-rule", "sale-route-rule"] # 应用于该数据源的 table route 规则
    filter-rules: ["store-filter-rule", "sale-filter-rule"] # 应用于该数据源的 binlog event filter 规则
    block-allow-list:  "log-bak-ignored" # 应用于该数据源的 Block & Allow Lists 规则
  -
    source-id: "instance-2"
    route-rules: ["store-route-rule", "sale-route-rule"]
    filter-rules: ["store-filter-rule", "sale-filter-rule"]
    block-allow-list:  "log-bak-ignored"

# 所有实例共享的其他通用配置

routes:
  store-route-rule:
    schema-pattern: "store_*"
    target-schema: "store"
  sale-route-rule:
    schema-pattern: "store_*"
    table-pattern: "sale_*"
    target-schema: "store"
    target-table:  "sale"

filters:
  sale-filter-rule:
    schema-pattern: "store_*"
    table-pattern: "sale_*"
    events: ["truncate table", "drop table", "delete"]
    action: Ignore
  store-filter-rule:
    schema-pattern: "store_*"
    events: ["drop database"]
    action: Ignore

block-allow-list:
  log-bak-ignored:
    do-dbs: ["user", "store_*"]
    ignore-tables:
    - db-name: "user"
      tbl-name: "log_bak"
文档内容是否有帮助?