从 TiDB 集群迁移数据至另一 TiDB 集群

    • 拆库:原 TiDB 集群体量过大,或者为了避免原有的 TiDB 集群所承载的数个业务之间互相影响,将原 TiDB 集群中的部分表迁到另一个 TiDB 集群。
    • 迁库:是对数据库的物理位置进行迁移,比如更换数据中心。
    • 升级:在对数据正确性要求严苛的场景下,可以将数据迁移到一个更高版本的 TiDB 集群,确保数据安全。

    本文将模拟整个迁移过程,具体包括以下四个步骤:

    1. 搭建环境
    2. 迁移全量数据
    3. 迁移增量数据
    4. 平滑切换业务
    1. 部署集群。

      使用 TiUP Playground 快速部署上下游测试集群。更多部署信息,请参考 。

    2. 初始化数据。

      测试集群中默认创建了 test 数据库,因此可以使用 sysbench 工具生成测试数据,用以模拟真实集群中的历史数据。

      这里通过 sysbench 运行 oltp_write_only 脚本,其将在测试数据库中生成 10 张表,每张表包含 10000 行初始数据。tidb-config 的配置如下:

      1. mysql-host=172.16.6.122 # 这里需要替换为实际上游集群 ip
      2. mysql-port=4000
      3. mysql-user=root
      4. mysql-password=
      5. db-driver=mysql # 设置数据库驱动为 mysql
      6. mysql-db=test # 设置测试数据库为 test
      7. report-interval=10 # 设置定期统计的时间间隔为 10 秒
      8. threads=10 # 设置 worker 线程数量为 10
      9. time=0 # 设置脚本总执行时间,0 表示不限制
      10. rate=100 # 设置平均事务速率 tps = 100
    3. 模拟业务负载。

      实际生产集群的数据迁移过程中,通常原集群还会写入新的业务数据,本文中可以通过 sysbench 工具模拟持续的写入负载,下面的命令会使用 10 个 worker 在数据库中的 sbtest1、sbtest2 和 sbtest3 三张表中持续写入数据,其总 tps 限制为 100。

      1. sysbench oltp_write_only --config-file=./tidb-config --tables=3 run
    4. 准备外部存储。

      在全量数据备份中,上下游集群均需访问备份文件,因此推荐使用存储备份文件,本文中通过 Minio 模拟兼容 S3 的存储服务:

      1. wget https://dl.min.io/server/minio/release/linux-amd64/minio
      2. chmod +x minio
      3. # 配置访问 minio 的 access-key access-screct-id
      4. export HOST_IP='172.16.6.122' # 替换为实际上游集群 ip
      5. export MINIO_ROOT_USER='minio'
      6. export MINIO_ROOT_PASSWORD='miniostorage'
      7. # 创建数据目录, 其中 backup 为 bucket 的名称
      8. mkdir -p data/backup
      9. # 启动 minio, 暴露端口在 6060
      10. ./minio server ./data --address :6060 &

      上述命令行启动了一个单节点的 minio server 模拟 S3 服务,其相关参数为:

      相应的访问链接为:

      1. s3://backup?access-key=minio&secret-access-key=miniostorage&endpoint=http://${HOST_IP}:6060&force-path-style=true

    搭建好测试环境后,可以使用 BR 工具的备份和恢复功能迁移全量数据。BR 工具有多种,本文中使用 SQL 语句 BACKUP 和 进行备份恢复。

    注意

    • 上下游集群版本不一致时,应检查 BR 工具的兼容性。本文假设上下游集群版本相同。

    1. 关闭 GC。

      为了保证增量迁移过程中新写入的数据不丢失,在开始备份之前,需要关闭上游集群的垃圾回收 (GC) 机制,以确保系统不再清理历史数据。

      执行如下命令关闭 GC:

      1. MySQL [test]> SET GLOBAL tidb_gc_enable=FALSE;
      1. Query OK, 0 rows affected (0.01 sec)

      查询 tidb_gc_enable 的取值,判断 GC 是否已关闭:

      1. +-------------------------+:
      2. | @@global.tidb_gc_enable |
      3. +-------------------------+
      4. | 0 |
      5. +-------------------------+
      6. 1 row in set (0.00 sec)
    2. 备份数据。

      在上游集群中执行 BACKUP 语句备份数据:

      1. +---------------+----------+--------------------+---------------------+---------------------+
      2. | Destination | Size | BackupTS | Queue Time | Execution Time |
      3. +---------------+----------+--------------------+---------------------+---------------------+
      4. | s3://backup | 10315858 | 431434047157698561 | 2022-02-25 19:57:59 | 2022-02-25 19:57:59 |
      5. +---------------+----------+--------------------+---------------------+---------------------+
      6. 1 row in set (2.11 sec)

      备份语句提交成功后,TiDB 会返回关于备份数据的元信息,这里需要重点关注 BackupTS,它意味着该时间点之前数据会被备份,后边的教程中,本文将使用 BackupTS 作为数据校验截止时间TiCDC 增量扫描的开始时间

    3. 恢复数据。

      在下游集群中执行 RESTORE 语句恢复数据:

      1. mysql> RESTORE DATABASE * FROM 's3://backup?access-key=minio&secret-access-key=miniostorage&endpoint=http://${HOST_IP}:6060&force-path-style=true';
      1. +--------------+-----------+--------------------+---------------------+---------------------+
      2. | Destination | Size | BackupTS | Queue Time | Execution Time |
      3. +--------------+-----------+--------------------+---------------------+---------------------+
      4. | s3://backup | 10315858 | 431434141450371074 | 2022-02-25 20:03:59 | 2022-02-25 20:03:59 |
      5. +--------------+-----------+--------------------+---------------------+---------------------+
      6. 1 row in set (41.85 sec)
    4. (可选)校验数据。

      1. sync_diff_inspector -C ./config.yaml

      关于 sync-diff-inspector 的配置方法,请参考,在本文中,相应的配置如下:

      1. # Diff Configuration.
      2. ######################### Datasource config #########################
      3. [data-sources]
      4. [data-sources.upstream]
      5. host = "172.16.6.122" # 需要替换为实际上游集群 ip
      6. port = 4000
      7. user = "root"
      8. password = ""
      9. snapshot = "431434047157698561" # 配置为实际的备份时间点(参见「备份」小节的 BackupTS)
      10. [data-sources.downstream]
      11. host = "172.16.6.125" # 需要替换为实际下游集群 ip
      12. port = 4000
      13. user = "root"
      14. password = ""
      15. ######################### Task config #########################
      16. [task]
      17. output-dir = "./output"
      18. target-instance = "downstream"
      19. target-check-tables = ["*.*"]
    1. 部署 TiCDC。

      完成全量数据迁移后,就可以部署并配置 TiCDC 集群同步增量数据,实际生产集群中请参考 。本文在创建测试集群时,已经启动了一个 TiCDC 节点,因此可以直接进行 changefeed 的配置。

    2. 创建同步任务。

      在上游集群中,执行以下命令创建从上游到下游集群的同步链路:

      以上命令中:

      • --server:TiCDC 集群中任意一个节点的地址
      • --sink-uri:同步任务下游的地址
      • --changefeed-id:同步任务的 ID,格式需要符合正则表达式 ^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$
      • --start-ts:TiCDC 同步的起点,需要设置为实际的备份时间点,也就是第 2 步:迁移全量数据中 “备份数据” 提到的 BackupTS

      更多关于 changefeed 的配置,请参考 。

    3. 重新开启 GC。

      TiCDC 可以保证 GC 只回收已经同步的历史数据。因此,创建完从上游到下游集群的 changefeed 之后,就可以执行如下命令恢复集群的垃圾回收功能。详情请参考 TiCDC GC safepoint 的完整行为

      执行如下命令打开 GC:

      1. MySQL [test]> SET GLOBAL tidb_gc_enable=TRUE;
      1. Query OK, 0 rows affected (0.01 sec)

      查询 tidb_gc_enable 的取值,判断 GC 是否已开启:

      1. MySQL [test]> SELECT @@global.tidb_gc_enable;
      1. +-------------------------+
      2. | @@global.tidb_gc_enable |
      3. +-------------------------+
      4. | 1 |
      5. +-------------------------+
      6. 1 row in set (0.00 sec)

    通过 TiCDC 创建上下游的同步链路后,原集群的写入数据会以非常低的延迟同步到新集群,此时可以逐步将读流量迁移到新集群了。观察一段时间,如果新集群表现稳定,就可以将写流量接入新集群,步骤如下:

    1. 停止上游集群的写业务。确认上游数据已全部同步到下游后,停止上游到下游集群的 changefeed。

      1. # 停止旧集群到新集群的 changefeed
      2. tiup cdc cli changefeed pause -c "upstream-to-downstream" --server=http://172.16.6.122:8300
      3. # 查看 changefeed 状态
      4. tiup cdc cli changefeed list
      1. [
      2. {
      3. "id": "upstream-to-downstream",
      4. "summary": {
      5. "state": "stopped", # 需要确认这里的状态为 stopped
      6. "tso": 431747241184329729,
      7. "checkpoint": "2022-03-11 15:50:20.387", # 确认这里的时间晚于停写的时间
      8. "error": null
      9. }
      10. }
      11. ]
    2. 将写业务迁移到下游集群,观察一段时间后,等新集群表现稳定,便可以弃用原集群。