一、数据迁移概述
1.1 迁移场景
从传统数据库迁移到 TiDB 是常见需求,主要场景包括:
| 场景 | 工具 | 数据量级 |
|---|---|---|
| 小数据量 MySQL 迁移 | Dumpling + TiDB Lightning | < 500GB |
| 大数据量 MySQL 迁移 | DM(Data Migration) | 500GB ~ 数 TB |
| 全量 + 增量同步 | DM | 持续同步 |
| CSV/SQL 文件导入 | TiDB Lightning | 任何来源 |
| 逻辑导出导入 | Dumpling + mysql client | 小数据量 |
| 持续复制到 Kafka | TiCDC | 实时 CDC |
1.2 迁移流程概览
完整迁移流程:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 评估兼容性 │───>│ 全量迁移 │───>│ 增量同步 │───>│ 切换流量 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │
v v v v
检查不兼容 导出数据 追平增量 验证数据
特性列表 导入 TiDB 数据校验 切换应用
二、使用 Dumpling + TiDB Lightning 迁移
2.1 Dumpling 导出
Dumpling 是 PingCAP 开发的逻辑备份工具,兼容 MySQL 协议:
# 从 MySQL 导出数据
dumpling \
-h 127.0.0.1 \
-P 3306 \
-u root \
-p 'password' \
-B mydb \
-f 'mydb.table[1-9]' \
-r 200000 \
-t 8 \
-o /data/export/mydb
参数说明:
| 参数 | 含义 |
|---|---|
-B |
导出指定数据库 |
-f |
过滤规则(只导出特定表) |
-r |
每个文件的最大行数(用于并发) |
-t |
并发线程数 |
-o |
输出目录 |
导出结果:
/data/export/mydb/
├── mydb-schema.sql # 建表语句
├── mydb.users.000000000.sql # 表数据(按行切分)
├── mydb.users.000000001.sql
├── mydb.orders.000000000.sql
└── metadata # 元信息(binlog 位置等)
2.2 TiDB Lightning 导入
TiDB Lightning 是高性能的全量数据导入工具:
# 配置文件 lightning.toml
[lightning]
level = "info"
file = "tidb-lightning.log"
[tikv-importer]
# 导入模式: "local"(推荐,最快)或 "tidb"
backend = "local"
# 排序后的 KV 文件目录
sorted-kv-dir = "/tmp/sorted-kv"
[mydumper]
# 数据源目录
data-source-dir = "/data/export/mydb"
[mydumper.csv]
separator = ','
delimiter = '"'
header = false
[tidb]
# TiDB 连接信息
host = "127.0.0.1"
port = 4000
user = "root"
password = ""
status-port = 10080
# 导入
tiup tidb-lightning -config lightning.toml
导入模式对比:
| 模式 | 速度 | 对 TiKV 影响 | 适用场景 |
|---|---|---|---|
| Local | 最快(可达 100GB/小时) | 大(需要独占) | 大批量首次导入 |
| TiDB | 较慢 | 小(正常写入) | 增量导入或小数据量 |
2.3 校验数据
# 使用 sync-diff-inspector 校验数据(所有配置通过 config 文件指定)
tiup sync-diff-inspector -C config.yaml
# config.yaml
data-sources:
mysql_source:
host: "127.0.0.1"
port: 3306
user: "root"
password: "password"
tidb_target:
host: "127.0.0.1"
port: 4000
user: "root"
password: ""
task:
output-dir: "./output"
source-instances:
- mysql_source
target-instance: tidb_target
target-check-tables:
- "mydb.*"
三、使用 DM 从 MySQL 迁移
3.1 DM 简介
DM(Data Migration)是 TiDB 官方推出的数据迁移工具,支持:
- 全量迁移:从 MySQL 导出现有数据
- 增量同步:持续同步 MySQL 的 binlog 到 TiDB
- 分库分表合并:将多个 MySQL 实例的库表合并到 TiDB 的一张表
DM 架构:
┌─────────┐
│ DM-master│ 调度和管理
└────┬────┘
│
┌────┴────┐
v v
┌─────┐ ┌─────┐
│DM-W1│ │DM-W2│ Worker,执行迁移任务
└──┬──┘ └──┬──┘
│ │
v v
MySQL-1 MySQL-2 → TiDB
3.2 部署 DM 集群
# DM 集群拓扑
cat > dm-topology.yaml << EOF
master_servers:
- host: 10.0.1.50
port: 8261
worker_servers:
- host: 10.0.1.51
port: 8262
deploy_dir: "/dm-deploy"
data_dir: "/dm-data"
EOF
# 部署
tiup dm deploy dm-cluster v8.5.0 dm-topology.yaml
# 启动
tiup dm start dm-cluster
3.3 配置迁移任务
# task.yaml — 全量 + 增量迁移配置
name: "mysql-to-tidb"
task-mode: "all" # all = 全量 + 增量
# 上游 MySQL 配置
mysql-instances:
- source-id: "mysql-source-1"
block-allow-list: "global"
mydumper-config-name: "global"
loader-config-name: "global"
syncer-config-name: "global"
# 下游 TiDB 配置
target-database:
host: "10.0.1.14"
port: 4000
user: "root"
password: ""
# 库表过滤
block-allow-list:
global:
do-dbs: ["mydb"]
do-tables:
- db-name: "mydb"
tbl-name: "~^table_.*" # 正则匹配
# 全量导出配置
mydumper-config-name: "global"
mydumpers:
global:
rows: 200000
threads: 8
# 全量导入配置
loader-config-name: "global"
loaders:
global:
pool-size: 16
dir: "./dumped_data"
# 增量同步配置
syncer-config-name: "global"
syncers:
global:
worker-count: 16
batch: 100
3.4 启动迁移任务
# 配置数据源
cat > source1.yaml << EOF
source-id: "mysql-source-1"
from:
host: "192.168.1.100"
port: 3306
user: "root"
password: "password"
EOF
# 创建数据源
tiup dmctl --master-addr 10.0.1.50:8261 operate-source create source1.yaml
# 启动迁移任务
tiup dmctl --master-addr 10.0.1.50:8261 start-task task.yaml
# 查看任务状态
tiup dmctl --master-addr 10.0.1.50:8261 query-status mysql-to-tidb
3.5 迁移过程中的操作
# 暂停任务
tiup dmctl --master-addr 10.0.1.50:8261 pause-task mysql-to-tidb
# 恢复任务
tiup dmctl --master-addr 10.0.1.50:8261 resume-task mysql-to-tidb
# 停止任务
tiup dmctl --master-addr 10.0.1.50:8261 stop-task mysql-to-tidb
# 查看当前 binlog 同步位置
tiup dmctl --master-addr 10.0.1.50:8261 query-status mysql-to-tidb
3.6 切换流量
当增量同步追平后,可以进行切换:
-- 1. 确认同步延迟为 0
-- 在 DM 中查看 syncer 的 binlog 位置
-- 对比 MySQL 当前的 binlog 位置
-- 2. 停止 MySQL 写入(在应用层)
-- 确保不再有新的写入
-- 3. 等待 DM 追平最后一个 binlog 事件
-- 4. 验证数据一致性
-- 使用 sync-diff-inspector
-- 5. 将应用连接切换到 TiDB
-- 6. 停止 DM 增量同步
tiup dmctl --master-addr 10.0.1.50:8261 stop-task mysql-to-tidb
四、分库分表合并迁移
4.1 场景
MySQL 分库分表:
db_order_01.order ─┐
db_order_02.order ─┼──> TiDB order_db.orders
db_order_03.order ─┘
每个库包含部分用户的数据,需要合并到 TiDB 的一张表中。
4.2 配置分库分表合并
# task-sharding.yaml
name: "sharding-merge"
task-mode: "all"
mysql-instances:
- source-id: "mysql-source-1"
block-allow-list: "global"
route-rules: ["db-route", "table-route"]
target-database:
host: "10.0.1.14"
port: 4000
user: "root"
# 路由规则:将多个库映射到一个库
routes:
db-route:
schema-pattern: "db_order_*"
target-schema: "order_db"
table-route:
schema-pattern: "db_order_*"
table-pattern: "order"
target-schema: "order_db"
target-table: "orders"
block-allow-list:
global:
do-dbs: ["db_order_*"]
do-tables:
- db-name: "db_order_*"
tbl-name: "order"
五、TiCDC 实时数据同步
5.1 TiCDC 简介
TiCDC 是 TiDB 的变更数据捕获(CDC)工具,可以实时捕获 TiDB 的数据变更并同步到下游:
TiCDC 架构:
┌────────┐ TiCDC ┌─────────┐
│ TiDB │ ──────────> │ Kafka │
│ (上游) │ Changefeed └─────────┘
└────────┘
│
v
┌─────────┐
│ MySQL │ (下游,可以是另一个 TiDB)
└─────────┘
│
v
┌─────────────┐
│ 云存储服务 │ (S3, GCS, 等)
└─────────────┘
5.2 部署 TiCDC
TiCDC 通常与 TiDB 集群一起部署:
# topology.yaml 中添加
cdc_servers:
- host: 10.0.1.60
port: 8300
deploy_dir: "/tidb-deploy/cdc-8300"
- host: 10.0.1.61
port: 8300
deploy_dir: "/tidb-deploy/cdc-8300"
tiup cluster scale-out tidb-cluster cdc-topology.yaml
5.3 创建 Changefeed
# 创建同步到 MySQL 的 Changefeed
# 注意:v7.0+ 已移除 --sort-engine 参数(unified sorter 成为默认)
tiup cdc cli changefeed create \
--server=http://10.0.1.60:8300 \
--sink-uri="mysql://root:password@10.0.2.100:3306/?time-zone=UTC" \
--changefeed-id="tidb-to-mysql"
# 创建同步到 Kafka 的 Changefeed
tiup cdc cli changefeed create \
--server=http://10.0.1.60:8300 \
--sink-uri="kafka://10.0.2.200:9092/tidb-cdc-topic?protocol=canal-json" \
--changefeed-id="tidb-to-kafka"
# 同步到云存储(S3)
tiup cdc cli changefeed create \
--server=http://10.0.1.60:8300 \
--sink-uri="s3://bucket-name/prefix?access-key=xxx&secret-access-key=xxx" \
--changefeed-id="tidb-to-s3"
5.4 管理 Changefeed
# 查看所有 Changefeed
tiup cdc cli changefeed list --server=http://10.0.1.60:8300
# 查看特定 Changefeed 状态
tiup cdc cli changefeed query --server=http://10.0.1.60:8300 \
--changefeed-id tidb-to-mysql
# 暂停 Changefeed
tiup cdc cli changefeed pause --server=http://10.0.1.60:8300 \
--changefeed-id tidb-to-mysql
# 恢复 Changefeed
tiup cdc cli changefeed resume --server=http://10.0.1.60:8300 \
--changefeed-id tidb-to-mysql
# 删除 Changefeed
tiup cdc cli changefeed remove --server=http://10.0.1.60:8300 \
--changefeed-id tidb-to-mysql
5.5 过滤同步的表
# 只同步特定库表(通过配置文件指定过滤规则)
# filter-rules.toml:
# [filter]
# rules = ["mydb.*", "!mydb.logs_*"]
tiup cdc cli changefeed create \
--server=http://10.0.1.60:8300 \
--sink-uri="mysql://root@10.0.2.100:3306/" \
--changefeed-id="filtered-sync" \
--config filter-rules.toml
# 同步 mydb 下所有表,除了 logs_ 开头的表
六、迁移中的兼容性问题
6.1 常见不兼容特性
| MySQL 特性 | TiDB 支持情况 | 处理方式 |
|---|---|---|
| 存储过程 | 有限支持 | 迁移到应用层 |
| 触发器 | 不支持 | 迁移到应用层 |
| 外键 | 6.x 起支持 | 检查版本 |
| 空间索引 | 有限支持 | 评估影响 |
| 全文索引 | 6.0+ 支持 | 检查版本 |
| 自定义函数 | 不支持 | 迁移到应用层 |
| VIEW | 支持 | 一般无需处理 |
6.2 迁移前检查
# 使用 DM 的前置检查
tiup dmctl --master-addr 10.0.1.50:8261 check-task task.yaml
6.3 数据类型映射
| MySQL 类型 | TiDB 类型 | 说明 |
|---|---|---|
| INT | INT | 完全兼容 |
| VARCHAR | VARCHAR | 完全兼容 |
| TIMESTAMP | TIMESTAMP | 行为一致 |
| ENUM | ENUM | 兼容 |
| SET | SET | 兼容 |
| GEOMETRY | GEOMETRY | 6.0+ 支持 |
七、迁移最佳实践
7.1 迁移前
- 评估兼容性:检查不兼容特性并制定方案
- 压测 TiDB:验证 TiDB 能承受目标负载
- 准备回滚方案:确保迁移失败时能回退到 MySQL
- 选择低峰窗口:减少对用户的影响
7.2 迁移中
- 先做全量迁移:导出 MySQL 数据,导入 TiDB
- 开启增量同步:追平全量后的增量数据
- 多次演练:在测试环境完整演练迁移流程
- 数据校验:使用 sync-diff-inspector 验证一致性
7.3 切换时
- 停止 MySQL 写入:在应用层面停写
- 等待追平:等增量同步延迟为 0
- 最终校验:再次确认数据一致
- 切换 DNS/连接串:将应用指向 TiDB
- 观察运行:监控 TiDB 状态,确认正常
7.4 切换后
- 保留 MySQL 数据:至少保留 1-2 周作为备份
- 持续监控:关注性能指标和错误日志
- 逐步优化:根据实际负载调整配置