1. 方案概述
1.1 背景与目标
华为云 GaussDB/RDS MySQL 是企业常用的关系型数据库服务,TiDB 作为分布式 HTAP 数据库,在以下场景具备明显优势:
| 维度 | 华为云 RDS/GaussDB | TiDB |
|---|---|---|
| 架构 | 单机/主从 | 分布式水平扩展 |
| 扩展性 | 垂直扩展为主 | 水平扩展,无上限 |
| HTAP | 不支持 | 原生支持(TiKV + TiFlash) |
| 兼容性 | MySQL 协议 | MySQL 协议(高度兼容) |
| 高可用 | 主备切换 | Multi-Raft,自动容灾 |
| 数据量 | TB 级遇瓶颈 | PB 级 |
迁移目标:
- 业务不中断,RPO = 0,RTO < 30min
- 数据零丢失,迁移前后一致性 100%
- 应用改动量最小化
1.2 迁移架构图
华为云 TiDB 集群
┌──────────────────┐ ┌──────────────────────────────────┐
│ RDS/GaussDB │ │ TiDB (SQL Layer) x3 │
│ ┌────────────┐ │ DM/Dumpling │ TiKV (Storage) x3+ │
│ │ 主库 │──┼──────────────▶│ TiFlash (HTAP) x2 (可选) │
│ │ binlog on │ │ │ PD (调度) x3 │
│ └────────────┘ │ └──────────────────────────────────┘
│ 从库/只读副本 │ ▲
└──────────────────┘ TiDB Data Migration (DM)
1.3 迁移流程总览
阶段一:评估 阶段二:准备 阶段三:迁移 阶段四:割接
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 兼容性 │ │ TiDB │ │ Schema │ │ 双写验证 │
│ 评估 │───────▶│ 集群部署 │───────▶│ + 全量 │───────▶│ 流量切换 │
│ 容量规划 │ │ 环境配置 │ │ + 增量 │ │ 业务验证 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
1~2 周 1 周 3~7 天 1 天
2. 迁移前评估
2.1 兼容性评估
2.1.1 使用 TiDB 兼容性检查工具
bash
复制
# 安装 tidb-tools
wget https://download.pingcap.org/tidb-community-toolkit-v7.5.0-linux-amd64.tar.gz
tar -xzf tidb-community-toolkit-v7.5.0-linux-amd64.tar.gz
# 使用 sqldiff 工具检查 DDL 兼容性
./tidb-tools/bin/checker \
-host <华为云RDS地址> \
-port 3306 \
-user root \
-password <密码> \
-database <库名>
2.1.2 手动检查清单
(1)不兼容的数据类型
sql
复制
-- 检查 ENUM/SET 类型(TiDB 支持,但有细微差异)
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_db'
AND (COLUMN_TYPE LIKE 'enum%' OR COLUMN_TYPE LIKE 'set%');
-- 检查空间类型(TiDB 部分支持)
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_db'
AND COLUMN_TYPE IN ('geometry','point','linestring','polygon','multipoint','multilinestring','multipolygon','geometrycollection');
(2)检查存储引擎
sql
复制
-- TiDB 只支持 InnoDB,MyISAM 需要提前处理
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_db'
AND ENGINE != 'InnoDB';
(3)检查不支持的 SQL 特性
sql
复制
-- 检查外键约束(TiDB 默认不强制外键)
SELECT TABLE_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'your_db'
AND REFERENCED_TABLE_NAME IS NOT NULL;
-- 检查触发器
SELECT TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE
FROM information_schema.TRIGGERS
WHERE TRIGGER_SCHEMA = 'your_db';
-- 检查存储过程
SELECT ROUTINE_NAME, ROUTINE_TYPE
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA = 'your_db';
(4)检查字符集
sql
复制
-- TiDB 推荐 utf8mb4
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_db'
AND TABLE_COLLATION NOT LIKE 'utf8mb4%';
2.1.3 已知不兼容项处理
| 华为云特性 | TiDB 处理方式 |
|---|---|
| 外键约束 | set @@foreign_key_checks=0 跳过,业务层保证完整性 |
| 存储过程/函数 | 逐一评估,部分可直接迁移,复杂逻辑改写 |
| 触发器 | 迁移至应用层或使用 TiDB 的 TTL/CDC 替代 |
| MyISAM 表 | 导出数据并以 InnoDB 重建 |
GROUP BY 隐式排序 |
显式加 ORDER BY |
ONLY_FULL_GROUP_BY |
TiDB 默认开启,SQL 需整改 |
| 自增 ID 不连续 | TiDB 分布式自增,不保证连续但单调递增 |
2.2 容量规划
sql
复制
-- 在华为云上评估数据量
SELECT
table_schema AS '数据库',
ROUND(SUM(data_length + index_length) / 1024 / 1024 / 1024, 2) AS '总大小(GB)',
ROUND(SUM(data_length) / 1024 / 1024 / 1024, 2) AS '数据(GB)',
ROUND(SUM(index_length) / 1024 / 1024 / 1024, 2) AS '索引(GB)',
COUNT(*) AS '表数量'
FROM information_schema.TABLES
WHERE table_schema NOT IN ('information_schema','mysql','performance_schema','sys')
GROUP BY table_schema;
-- 评估每日数据增量(通过 binlog 大小估算)
SHOW BINARY LOGS;
TiDB 集群容量建议(TiKV 3 副本):
| 华为云数据量 | TiKV 节点数 | 单节点配置 |
|---|---|---|
| < 500GB | 3 节点 | 16C/64G/2TB SSD |
| 500GB~2TB | 3~6 节点 | 32C/128G/4TB SSD |
| 2TB~10TB | 6~12 节点 | 32C/128G/8TB SSD |
| > 10TB | 12+ 节点 | 按需扩展 |
2.3 网络与权限评估
sql
复制
-- 确认华为云 RDS 开启 binlog(迁移增量同步必需)
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';
-- 需要: log_bin=ON, binlog_format=ROW
-- 检查 binlog 保留时间(建议 >= 7 天)
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
-- 创建迁移专用账号(最小权限原则)
CREATE USER 'migration'@'%' IDENTIFIED BY 'StrongPassword123!';
GRANT SELECT, RELOAD, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'migration'@'%';
GRANT SELECT ON mysql.* TO 'migration'@'%';
3. 环境准备
3.1 TiDB 集群部署(使用 TiUP)
bash
复制
# 1. 安装 TiUP(在中控机执行)
curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
source ~/.bash_profile
# 2. 安装集群组件
tiup cluster
# 3. 编写集群拓扑配置文件 topology.yaml
cat > topology.yaml << 'EOF'
global:
user: "tidb"
ssh_port: 22
deploy_dir: "/tidb-deploy"
data_dir: "/tidb-data"
server_configs:
tidb:
log.slow-threshold: 300
binlog.enable: false
performance.max-procs: 0
tikv:
rocksdb.max-open-files: -1
raftstore.capacity: 0
storage.reserve-space: 0
pd:
schedule.leader-schedule-limit: 4
schedule.region-schedule-limit: 2048
schedule.replica-schedule-limit: 64
pd_servers:
- host: 10.0.1.11
- host: 10.0.1.12
- host: 10.0.1.13
tidb_servers:
- host: 10.0.1.21
port: 4000
status_port: 10080
- host: 10.0.1.22
port: 4000
status_port: 10080
- host: 10.0.1.23
port: 4000
status_port: 10080
tikv_servers:
- host: 10.0.1.31
port: 20160
status_port: 20180
config:
server.labels:
zone: "zone1"
- host: 10.0.1.32
port: 20160
status_port: 20180
config:
server.labels:
zone: "zone2"
- host: 10.0.1.33
port: 20160
status_port: 20180
config:
server.labels:
zone: "zone3"
# 如需 HTAP 功能,添加 TiFlash
tiflash_servers:
- host: 10.0.1.41
data_path: /tiflash-data
monitoring_servers:
- host: 10.0.1.51
grafana_servers:
- host: 10.0.1.51
EOF
# 4. 部署集群
tiup cluster deploy tidb-prod v7.5.0 topology.yaml --user root -p
# 5. 启动集群
tiup cluster start tidb-prod
# 6. 验证集群状态
tiup cluster display tidb-prod
3.2 华为云侧准备
sql
复制
-- 1. 确认开启 GTID(推荐,便于增量同步断点恢复)
SHOW VARIABLES LIKE 'gtid_mode';
SHOW VARIABLES LIKE 'enforce_gtid_consistency';
-- 如果未开启,需要配合 DBA 在维护窗口开启
-- 2. 创建迁移用户
CREATE USER 'dm_user'@'%' IDENTIFIED BY 'DmPassword@2024';
GRANT SELECT, RELOAD, LOCK TABLES, REPLICATION SLAVE,
REPLICATION CLIENT, TRIGGER ON *.* TO 'dm_user'@'%';
FLUSH PRIVILEGES;
-- 3. 确认网络互通(在迁移机器上测试)
mysql -h <华为云RDS地址> -P 3306 -u dm_user -p -e "SELECT 1"
mysql -h <TiDB地址> -P 4000 -u root -p -e "SELECT 1"
4. Schema 迁移
4.1 导出 Schema
bash
复制
# 使用 Dumpling 导出 Schema(仅结构,不含数据)
tiup dumpling \
-h <华为云RDS地址> \
-P 3306 \
-u dm_user \
-p "DmPassword@2024" \
--filetype sql \
--no-data \
-o /data/schema_dump \
--databases your_db1,your_db2
4.2 Schema 调整脚本
bash
复制
cat > fix_schema.sh << 'SCRIPT'
#!/bin/bash
SCHEMA_DIR="/data/schema_dump"
OUTPUT_DIR="/data/schema_fixed"
mkdir -p $OUTPUT_DIR
for f in $SCHEMA_DIR/*.sql; do
filename=$(basename "$f")
content=$(cat "$f")
# 1. MyISAM → InnoDB
content=$(echo "$content" | sed 's/ENGINE=MyISAM/ENGINE=InnoDB/g')
# 2. 去掉 AUTO_INCREMENT 具体值
content=$(echo "$content" | sed 's/ AUTO_INCREMENT=[0-9]*//g')
# 3. charset 统一为 utf8mb4
content=$(echo "$content" | sed 's/DEFAULT CHARSET=latin1/DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci/g')
content=$(echo "$content" | sed 's/DEFAULT CHARSET=utf8\b/DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci/g')
# 4. 去掉 COMPRESSION 选项
content=$(echo "$content" | sed "s/COMPRESSION='[^']*'//g")
echo "$content" > "$OUTPUT_DIR/$filename"
done
echo "Schema 调整完成"
SCRIPT
chmod +x fix_schema.sh && ./fix_schema.sh
5. 数据迁移
5.1 方案选择
| 数据量 | 迁移时间窗口 | 推荐工具 | 备注 |
|---|---|---|---|
| < 100GB | 不限 | DM(全量+增量) | 简单易用 |
| 100GB~1TB | 有窗口 | TiDB Lightning + DM 增量 | 速度最快 |
| > 1TB | 有窗口 | TiDB Lightning + DM 增量 | 必选 |
| 任意 | 无停机 | DM 全量+增量一体 | 推荐生产环境 |
5.2 方案 A:TiDB DM(推荐)
bash
复制
# 部署 DM 集群
cat > dm-topology.yaml << 'EOF'
master_servers:
- host: 10.0.1.61
port: 8261
peer_port: 8291
worker_servers:
- host: 10.0.1.62
port: 8262
- host: 10.0.1.63
port: 8262
EOF
tiup dm deploy dm-cluster v7.5.0 dm-topology.yaml --user root -p
tiup dm start dm-cluster
yaml
复制
# task-migration.yaml
name: "huawei-to-tidb"
task-mode: all # all = 全量 + 增量
target-database:
host: "<TiDB地址>"
port: 4000
user: "root"
password: ""
mysql-instances:
- source-id: "huawei-rds-01"
route-rules: ["route-rule-1"]
filter-rules: ["filter-1"]
mydumper-config-name: "dump-config"
loader-config-name: "load-config"
syncer-config-name: "sync-config"
routes:
route-rule-1:
schema-pattern: "your_db1"
target-schema: "your_db1"
filters:
filter-1:
schema-pattern: "your_db1"
events: ["truncate table", "drop table", "drop database"]
action: Ignore
syncers:
sync-config:
worker-count: 16
batch: 100
safe-mode: true
bash
复制
# 启动 / 监控 / 管理任务
tiup dmctl --master-addr 10.0.1.61:8261 start-task task-migration.yaml
tiup dmctl --master-addr 10.0.1.61:8261 query-status huawei-to-tidb
tiup dmctl --master-addr 10.0.1.61:8261 pause-task huawei-to-tidb
tiup dmctl --master-addr 10.0.1.61:8261 resume-task huawei-to-tidb
5.3 方案 B:TiDB Lightning(大数据量)
bash
复制
# Step 1: Dumpling 导出
tiup dumpling -h <华为云RDS地址> -P 3306 -u dm_user -p "DmPassword@2024" \
--consistency=flush --filetype parquet -o /data/full_dump \
--databases your_db1,your_db2 -r 200000 --threads 8
# Step 2: Lightning 导入
cat > lightning.toml << 'EOF'
[tikv-importer]
backend = "local"
sorted-kv-dir = "/tmp/sorted-kv-dir"
[mydumper]
data-source-dir = "/data/full_dump"
[tidb]
host = "<TiDB地址>"
port = 4000
user = "root"
pd-addr = "10.0.1.11:2379"
[post-restore]
checksum = "required"
analyze = "optional"
EOF
tiup tidb-lightning -config lightning.toml
6. 应用层改造
6.1 连接配置(Spring Boot 示例)
properties
复制
# 修改后(TiDB 推荐参数)
spring.datasource.url=jdbc:mysql://<TiDB地址>:4000/your_db\
?useSSL=false\
&characterEncoding=UTF-8\
&useServerPrepStmts=true\
&cachePrepStmts=true\
&rewriteBatchedStatements=true\
&connectionCollation=utf8mb4_unicode_ci
6.2 关键 SQL 改造点
sql
复制
-- ① 游标分页替代大 offset
-- 改前
SELECT * FROM orders ORDER BY id LIMIT 100000, 20;
-- 改后
SELECT * FROM orders WHERE id > :last_id ORDER BY id LIMIT 20;
-- ② 批量 DELETE 分批执行(事务大小限制)
DELETE FROM logs WHERE create_time < '2023-01-01' LIMIT 1000;
-- 循环执行直到影响行数为 0
-- ③ 写热点:AUTO_RANDOM 替代 AUTO_INCREMENT
CREATE TABLE orders (
id BIGINT AUTO_RANDOM PRIMARY KEY,
user_id INT NOT NULL,
...
);
-- ④ HTAP 查询强制走 TiFlash
ALTER TABLE orders SET TIFLASH REPLICA 1;
SELECT /*+ read_from_storage(tiflash[orders]) */
DATE(created_at), SUM(amount)
FROM orders WHERE created_at >= '2024-01-01'
GROUP BY DATE(created_at);
7. 数据验证与一致性校验
7.1 sync-diff-inspector
bash
复制
cat > diff-config.toml << 'EOF'
check-thread-count = 4
export-fix-sql = true
[task]
output-dir = "./output"
source-instances = ["huawei-rds"]
target-instance = "tidb-target"
target-check-tables = ["your_db1.*"]
[data-sources.huawei-rds]
host = "<华为云RDS地址>"
port = 3306
user = "dm_user"
password = "DmPassword@2024"
[data-sources.tidb-target]
host = "<TiDB地址>"
port = 4000
user = "root"
password = ""
EOF
./sync_diff_inspector --config=diff-config.toml
cat output/summary.txt # 如有差异生成 output/fix.sql
7.2 增量延迟监控
bash
复制
# 割接前延迟须稳定 < 3s
watch -n 5 'tiup dmctl --master-addr 10.0.1.61:8261 query-status huawei-to-tidb 2>/dev/null \
| python3 -c "import sys,json; d=json.load(sys.stdin); \
[print(\"Delay:\", s[\"sync\"][\"secondsBehindMaster\"], \"s\") for s in d[\"subTaskStatus\"]]"'
8. 性能调优
sql
复制
-- 统计信息收集(迁移完成后立即执行)
ANALYZE TABLE your_db1.orders;
SET GLOBAL tidb_auto_analyze_ratio = 0.1;
SET GLOBAL tidb_auto_analyze_start_time = '02:00 +0800';
SET GLOBAL tidb_auto_analyze_end_time = '06:00 +0800';
-- 热点 Region 处理
ALTER TABLE orders MODIFY id BIGINT AUTO_RANDOM;
ALTER TABLE logs SHARD_ROW_ID_BITS = 4 PRE_SPLIT_REGIONS = 4;
-- 检查热点 Region
SELECT region_id, written_bytes, read_bytes
FROM information_schema.TIKV_REGION_STATUS
ORDER BY written_bytes DESC LIMIT 10;
9. 割接方案
9.1 灰度切换时间线
T0 DM 增量同步运行中(华为云 → TiDB)
T1 切 5% 读流量到 TiDB,观察 15min
T2 逐步扩大:30% → 50% → 100%(每步间隔 30min)
T3 维护窗口(01:00~02:00):停写 → 等延迟为 0 → 切写流量 → 停 DM
T4 割接完成,华为云保留 7 天作回滚备用
9.2 割接操作脚本
bash
复制
# 1. 确认 DM 延迟
tiup dmctl --master-addr 10.0.1.61:8261 query-status huawei-to-tidb \
| grep -E "secondsBehindMaster|syncerBinlog"
# 2. 华为云设只读
mysql -h <华为云RDS地址> -P 3306 -u admin -p -e "SET GLOBAL read_only = ON;"
# 3. 等待同步追平
while true; do
DELAY=$(tiup dmctl --master-addr 10.0.1.61:8261 query-status huawei-to-tidb 2>/dev/null \
| python3 -c "import sys,json; d=json.load(sys.stdin); \
print(d['subTaskStatus'][0]['sync']['secondsBehindMaster'])" 2>/dev/null)
echo "延迟: ${DELAY}s"; [ "$DELAY" = "0" ] && break; sleep 5
done
# 4. 最终数据量校验
HW_CNT=$(mysql -h <华为云地址> -P 3306 -u dm_user -pDmPassword@2024 -se "SELECT COUNT(*) FROM your_db1.orders")
TD_CNT=$(mysql -h <TiDB地址> -P 4000 -u root -p -se "SELECT COUNT(*) FROM your_db1.orders")
[ "$HW_CNT" = "$TD_CNT" ] && echo "✅ 可以割接" || { echo "❌ 不一致!"; exit 1; }
# 5. 切写流量到 TiDB(修改负载均衡/DNS/应用配置)
# 6. 停止 DM 任务
tiup dmctl --master-addr 10.0.1.61:8261 stop-task huawei-to-tidb10. 回滚预案
10.1 触发条件
| 指标 | 警戒值 | 回滚触发 |
|---|---|---|
| 接口错误率 | > 0.5% | > 2% |
| P99 延迟 | > 原来 2x | > 原来 5x |
| DB 连接失败 | > 0 | 持续 > 30s |
10.2 回滚脚本
bash
复制
# 解除华为云只读
mysql -h <华为云RDS地址> -P 3306 -u admin -p -e "SET GLOBAL read_only = OFF;"
# 切流量回华为云(负载均衡/DNS)
# 启动反向同步(TiDB → 华为云,补偿割接后增量)
tiup dmctl --master-addr 10.0.1.61:8261 start-task task-reverse-sync.yaml
11. 监控与运维
bash
复制
# Grafana 监控(端口 3000)
# Dashboard:TiDB Summary / TiKV Details / PD
# 扩容 TiKV(不停业务)
tiup cluster scale-out tidb-prod scale-out-tikv.yaml
# 滚动升级
tiup cluster upgrade tidb-prod v7.6.0
# 备份(BR)
tiup br backup full \
--pd 10.0.1.11:2379 \
--storage s3://your-bucket/backup/$(date +%Y%m%d)
关键告警规则:
yaml
复制
groups:
- name: tidb-critical
rules:
- alert: TiDBDown
expr: up{job="tidb"} == 0
- alert: TiKVStorageUsage
expr: tikv_store_size_bytes{type="used"} / tikv_store_size_bytes{type="capacity"} > 0.8
for: 10m
- alert: TiDBSlowQuery
expr: histogram_quantile(0.99, rate(tidb_server_slow_query_process_duration_seconds_bucket[5m])) > 1
for: 5m
12. 常见问题 FAQ
Q1:DM 报 Error 1236: Could not find first log file name
bash
复制
# binlog 被清理,重置任务
tiup dmctl --master-addr 10.0.1.61:8261 stop-task huawei-to-tidb
tiup dmctl --master-addr 10.0.1.61:8261 delete-meta huawei-to-tidb
tiup dmctl --master-addr 10.0.1.61:8261 start-task task-migration.yaml
Q2:Lightning 报 checksum mismatch
bash
复制
tiup tidb-lightning-ctl --config lightning.toml --checkpoint-error-destroy=all
tiup tidb-lightning -config lightning.toml
Q3:写热点 TiKV 节点 CPU 100%
sql
复制
-- pre-split 分裂 Region
ALTER TABLE orders SPLIT BY (100000),(200000),(300000),(400000),(500000);
-- 或改用 AUTO_RANDOM
ALTER TABLE orders MODIFY id BIGINT AUTO_RANDOM;
Q4:何时可以停止 DM 任务
bash
复制
# 两侧 MAX(id) 相同且延迟为 0,即可安全停止
mysql -h <华为云地址> -se "SELECT MAX(id) FROM your_db1.orders"
mysql -h <TiDB地址> -P 4000 -se "SELECT MAX(id) FROM your_db1.orders"