MySQL 数据迁移(四)数据文件物理迁移实战与评测
逻辑迁移再怎么多线程,都绕不过 SQL 解析和 InnoDB 事务开销。物理迁移直接拷贝数据文件,让 MySQL'换壳不换魂'。本篇实战 XtraBackup 的 xbstream + qpress 流式压缩备份、prepare 恢复全流程,以及可传输表空间单表迁移方案,用同样的 2000 万行压测数据回答:物理迁移到底快多少,代价是什么。
文章信息
- 原文链接:https://jiayq.blog.csdn.net/article/details/166594750
- 发布时间:2026-09-24 15:36:46
- 标签:#mysql, #数据库, #mysqldump, #mydumper, #myloader, #DTS
MySQL 数据迁移(四)数据文件物理迁移实战与评测
摘要
逻辑迁移再怎么多线程,都绕不过 SQL 解析和 InnoDB 事务开销。物理迁移直接拷贝数据文件,让 MySQL”换壳不换魂”。本篇实战 XtraBackup 的 xbstream + qpress 流式压缩备份、prepare 恢复全流程,以及可传输表空间单表迁移方案,用同样的 2000 万行压测数据回答:物理迁移到底快多少,代价是什么。
1. 建议先看
- MySQL 数据迁移(一)为什么迁、怎么迁、怎么评——三大方案总览
- MySQL 数据迁移(二)原生 mysqldump 导出导入实战与评测
- MySQL 数据迁移(三)mydumper/myloader 多线程迁移实战与评测
2. 物理迁移的原理:绕过 SQL 层
2.1 逻辑迁移的天花板
前两篇的方案无论怎么并行,每行数据都要走一遍完整流程:
1
SQL 文本 → SQL 解析器 → 执行器 → 事务(binhlog+redo+fsync) → 数据页
物理迁移直接跳过前面所有环节,拷贝的就是 InnoDB 的数据页文件(.ibd/.ibdata)——目标端拿到的是”已经写好的数据页”,恢复本质是文件搬运 + 少量日志回放。
2.2 核心难题:数据文件在”动”,怎么拷才一致
直接 cp 数据目录是不行的:拷贝期间业务还在写,拷出来的是”不同时刻的页”混在一起的废数据。XtraBackup 的解法:
- 拷贝数据文件的同时,持续追踪 redo log(记录拷贝期间的新写入)
- 拷贝完成后,拿到一个一致性位点(执行
LOCK INSTANCE FOR BACKUP) - prepare 阶段:用记录下来的 redo 重放,把”拷贝期间发生的写入”补进拷贝的文件里,最终得到”位点时刻的一致性快照”
一句话:数据文件抄旧一点没关系,redo 日志会把差异补齐——这正是 MySQL 崩溃恢复(crash recovery)的原理复用。
2.3 xbstream 与 qpress:流式打包与压缩
- xbstream:XtraBackup 的流式归档格式,把备份产物(成百上千个文件)打包成单个流输出到 stdout,天然适合管道传输(
| ssh、上传对象存储) - qpress:QuickLZ 算法压缩器,XtraBackup 生态的标配(历史上配合
--compress内置压缩;XtraBackup 8.0 移除了内置压缩,改为外部工具压缩 xbstream 流)
1
2
3
4
5
6
7
# 导出:xbstream 流 → qpress 压缩 → 单个文件
xtrabackup --backup --stream=xbstream ... | qpress ... > backup.xbstream.qp
# 导入:qpress 解压 → xbstream 提取 → prepare
qpress -d backup.xbstream.qp ...
xbstream -x < backup.xbstream -C restore_dir
xtrabackup --prepare --target-dir=restore_dir
2.4 可传输表空间:单表级别的物理迁移
XtraBackup 迁移的是整个实例。如果只想迁一张表,用”可传输表空间”(Transportable Tablespace):
1
2
3
4
5
6
7
8
-- 源端:锁表刷盘,生成 .cfg 元数据文件
FLUSH TABLES sbtest1 FOR EXPORT;
-- 拷走 sbtest1.ibd + sbtest1.cfg 后解锁
UNLOCK TABLES;
-- 目标端:建同结构空表 → 丢弃自己的表空间 → 放入源端文件 → 导入
ALTER TABLE sbtest1 DISCARD TABLESPACE;
ALTER TABLE sbtest1 IMPORT TABLESPACE;
.cfg 文件记录了表结构指纹(列类型、顺序、表空间 ID),IMPORT TABLESPACE 时做严格校验——所以要求两端表结构完全一致(同版本建出的同 DDL)。
实测(sbtest1 表,100 万行 / 264MB 表空间文件):从源表 FLUSH TABLES ... FOR EXPORT 到目标端 IMPORT TABLESPACE 完成并校验(COUNT = 100 万),全程秒级完成——同样的表走 myloader 逻辑导入需要 8~10 秒。单表物理迁移的”导入”本质是让 InnoDB 直接认领一个已经写好的数据文件,264MB 的文件拷贝几乎就是全部成本。
两个实测踩坑(都是真实翻车现场):
.ibd文件拷入目标端后必须把属主改成mysql(docker 部署时是 uid 999),否则IMPORT TABLESPACE直接失败且报错信息不明确(mysqld 打不开文件)FLUSH TABLES ... FOR EXPORT会加表级读锁,锁期间这张表的写入全部阻塞——生产上对在线大表做这个操作要评估锁窗口
3. 实战:XtraBackup 全量迁移
3.1 备份
1
2
3
4
5
6
7
8
9
10
11
12
13
# 流式备份 + qpress 压缩(实测环境:Docker 部署 MySQL,xtrabackup 以容器方式运行)
docker run --rm --user=0 --network mysql-migrate-net \
-v /data/mysql-migrate/single/data:/var/lib/mysql:ro \
-v /backup:/backup \
percona/percona-xtrabackup:8.0.35 \
xtrabackup --backup \
--host=mysql-single --port=3306 \
--user=root --password=*** \
--stream=xbstream --parallel=4 \
> backup.xbstream
# qpress 压缩(跨机传输前打包)
qpress -c backup.xbstream backup.xbstream.qp
参数说明:
| 参数 | 作用 |
|---|---|
--stream=xbstream | 备份产物打包成流输出到 stdout,不落本地多文件 |
--parallel=4 | 数据文件拷贝并行度(多个文件同时拷) |
--host/--user | 连接实例拿位点、执行备份锁(注意:不连实例的文件级拷贝是错的) |
--user=0(docker) | 容器内以 root 运行,否则无权读数据目录 |
实测耗时与产物(2000 万行 / 20 张表,压测伴随,单节点组):
| 阶段 | 耗时 | 说明 |
|---|---|---|
| xtrabackup 流式备份 | 150s | 产物 xbstream 5298MB |
| qpress 压缩 | 39s | 3162MB(压缩比 0.59) |
| 备份期间源库压测 | TPS 396 → 261(-34%) | 物理备份的 IO 冲击显著大于逻辑导出 |
两个值得注意的数字:
- 物理产物(5298MB)比逻辑 SQL 裸文件(3810MB)还大:XtraBackup 拷贝的是 InnoDB 数据文件本身,包含空闲页、碎片页、undo 段、redo——”文件的物理体积”大于”数据的逻辑体积”。压缩后(3162MB)才反超 gzip 压缩的 SQL(1841MB),逻辑文本的压缩率天生好于二进制页文件
- 备份期间源库 TPS 掉了 34%:对比 mysqldump(+12%)和 mydumper(+4%)——物理备份一边顺序读全部数据文件、一边持续追踪 redo,IO 通道占得更满。这是”备份期间业务退化”最明显的一种
3.2 恢复:解压 → 提取 → prepare → 挂载
1
2
3
4
5
6
7
8
9
10
11
12
# 1. qpress 解压(注意:qpress -d 的目标参数是目录)
qpress -d backup.xbstream.qp /backup/
# 2. xbstream 提取成文件树
mkdir -p restore_dir
xbstream -x < backup.xbstream -C restore_dir
# 3. prepare:重放 redo,把备份推到一致性状态
xtrabackup --prepare --target-dir=restore_dir
# 4. 停目标实例 → 清空数据目录 → 拷入 → 改权限 → 启动
# (物理恢复是整实例替换,目标端必须是空实例!)
实测耗时分解(单节点组,同上):
| 恢复阶段 | 耗时 | 说明 |
|---|---|---|
| qpress 解压 + xbstream 提取 | 35s | 5298MB 文件树还原 |
| xtrabackup –prepare | 103s | 重放 redo,推到一致性位点 |
| 拷入 target + 重启就绪 | ~42s | 文件级 cp + 实例启动 |
| 恢复总计 | 180s | |
| 全流程(备份+压缩+恢复) | 369s | |
| 校验 | 20/20 表行数一致 | 物理恢复数据完整 |
3.3 验证与位点
恢复完成后,位点信息按 GTID 事务集合确认和衔接,三步闭环:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 备份产物里自带位点文件(file + pos + GTID 三种表达)
$ cat restore_dir/xtrabackup_binlog_info
mysql-bin.000007 1234567 3E11FA47-27CA-11EE-...:1-5000
# 2. 恢复后的实例自带完整的事务集合(与源库快照时刻一致)
mysql> SELECT @@GLOBAL.gtid_executed;
3E11FA47-27CA-11EE-...:1-5000
# 3. 增量追赶:GTID 复制直接接上源库,无需任何位点换算
mysql> CHANGE REPLICATION SOURCE TO
-> SOURCE_HOST='***', SOURCE_PORT=3306,
-> SOURCE_USER='repl', SOURCE_PASSWORD=***,
-> SOURCE_AUTO_POSITION=1;
mysql> START REPLICA;
一份数据三种位点表达:binlog 文件名、字节偏移、GTID 事务集合——第三列才是最有价值的。物理恢复出的实例自带完整的 gtid_executed(备份时刻源库已执行的事务集合,xtrabackup_binlog_info 的第三列),这是三个工具里位点信息最完整的一个:增量追赶直接 SOURCE_AUTO_POSITION=1,源库根据事务集合自动协商从哪里发,集合里已有的事务幂等跳过——不依赖 file+pos 那种脆弱的字节偏移(第二篇 2.4 的结论),也不需要像逻辑导出那样单独记录位点,恢复即得到。这也是物理迁移在”全量 + 增量”两阶段方案里天然好接的原因:恢复完的目标库在 GTID 维度上就是源库快照时刻的精确镜像。
4. 实验评测
4.1 三套架构结果总表
| 架构组 | 备份源 | 备份耗时 | 恢复耗时(含prepare) | 全流程 | 校验 |
|---|---|---|---|---|---|
| 单节点 | single 本机 | 150s / 5298MB | 180s | 369s | 20/20 表行数一致 |
| 主从 | master 主库 | 172s / 5291MB | 177s | 388s | 20/20 表行数一致 |
| 主从(补充组) | slave 纯备库 | 156s / 5169MB | 179s | 375s | 20/20 表行数一致 |
| 读写分离 | ro 只读节点 | 169s / 5298MB | 169s | 378s | 20/20 表行数一致 |
补充组(压测打 master、xtrabackup 从 slave 纯备库备份)的实测:master 压测 TPS 414.08 → 270.48(-35%)、P95 28.16 → 31.37ms;slave 自身复制延迟最高 53 秒;备份 156s、恢复 179s(extract 36s + prepare 106s + 拷入启动 37s)。
三组附加数据横向对比,结论非常清晰:
| 备份打在谁身上 | 主库压测 TPS 变化 | 副本自身复制延迟 |
|---|---|---|
| master 主库 | -38% | 从库 28s |
| slave 纯备库 | -35% | slave 53s(最高) |
| ro 只读节点 | -32% | ro 46s |
- 主从组:备份期间从库复制延迟最高 28 秒(均值 5.5s)——xtrabackup 占满 master 的 IO 通道后,binlog 下发和从库回放都被挤压。对比第三篇 mydumper 备份 master 时从库延迟仅 2 秒,物理备份对复制链路的冲击是逻辑导出的十倍量级
- 读写分离组:备份打 ro、压测打 rw,ro 自身复制延迟最高冲到 46 秒——ro 一边被备份占满 IO 一边回放主库 binlog,双重压力下延迟比”主库备份+从库旁观”场景(28s)还要高。而 rw 的压测 TPS 也掉了 32%:ro 的备份 IO 和 rw 共享同一台宿主机的磁盘带宽
- 纯备 slave 组(补充):延迟冲到 53 秒,是三组最高——备份 IO 与复制回放同时在 slave 上抢资源;主库 TPS 降 35%,与另外两组处于同一量级(-32%~-38%),说明物理备份对主库的影响主要经宿主机共享资源传导,而不是”备份打在哪个节点”决定的
- 结论:物理备份的”从哪个节点做”更加重要,因为它对备份节点和其复制链路的冲击都是三个工具里最重的。生产上物理备份节点要选”不接业务读流量、且允许分钟级复制延迟”的副本——主从架构里不接读流量的纯备 slave 正好符合(第二篇 4.4 决策树):slave 的分钟级延迟上升不影响任何业务读,代价是延迟峰值更高(53s),只要它不服务读流量就完全无害
恢复侧三个阶段分解(主从组):qpress 解压 + xbstream 提取 37s、prepare 102s、拷入并启动目标实例 38s。prepare 占恢复的一半时间——它在重放备份期间积压的 redo,把”拷贝时的旧文件”补齐到”位点时刻的一致性状态”。
4.2 备份期间源库冲击:物理 vs 逻辑
同数据集、同压测方法,三种工具备份/导出期间的源库表现(各组内对比):
| 工具 | 基线 TPS | 备份/导出期间 TPS | 组内变化 | P95 变化 |
|---|---|---|---|---|
| mysqldump(第二篇) | 257~261 | 269~288 | +4%~+12% | 持平或更好 |
| mydumper 8线程(第三篇) | 326~371 | 324~342 | -13%~+4% | +24%(rwro组) |
| xtrabackup(本篇) | 396 | 261 | -34% | +15% |
三条结论:
- 冲击强度:物理备份 > 多线程逻辑导出 > 单线程逻辑导出。直觉上”物理拷贝只是读文件”应该最轻,实测恰恰相反——顺序读满整个数据目录 + 持续追踪 redo 的双重 IO,比走 SQL 层的 SELECT 流式读更占磁盘带宽
- 前两篇的”TPS 不降反升”是数据集(4.2GB)≈ buffer pool(4G)的边界特例;xtrabackup 在这种特例下依然让 TPS 掉 34%,足见其 IO 占用强度
- 生产启发:物理备份更要挑低峰窗口。云厂商的物理备份策略普遍放在凌晨,原因就在这
4.3 三方案总对决
同数据集(2000 万行 / 4.2GB)全流程对比:
| 维度 | mysqldump | mydumper/myloader | xtrabackup |
|---|---|---|---|
| 全流程耗时 | 632s | 200s | 369s |
| 备份/导出 | 60s | 28s | 150s |
| 恢复/导入 | 529s | 172s | 180s |
| 传输产物大小 | 1841MB(gzip) | 1904MB(自带压缩) | 3162MB(qpress) |
| 备份期间源库 TPS | +12% | +4% | -34% |
| 恢复侧确定性 | SQL 逐行执行 | SQL 逐行执行(并行) | 文件级,与行数无关 |
| 目标端要求 | 库存在即可 | 库存在即可 | 空实例整库替换 |
| 版本兼容 | 最好 | 好 | 严苛 |
4.2GB 规模下的冠军是 mydumper/myloader(200s),物理方案(369s)反而居中。为什么”最快的物理迁移”没赢?
物理备份的耗时结构:拷贝 5.3GB 文件(150s)+ prepare 回放(103s)+ 文件级恢复(77s)——全部是”与数据体积成正比”的 IO 操作,与行数无关。而逻辑导入是”与行数成正比”的解析+执行操作。数据量小(行数少、体积小)时,逻辑导入的行级成本被多线程摊薄,物理方案的固定 IO 开销反而更重。
交叉点在哪? 逻辑导入的行成本是刚性的(每行都要过解析器),数据量翻倍导入时间近似翻倍;物理备份的时间近似只随数据体积线性增长。实测 myloader 导入速度约 116,000 行/s,在单机实验规格下,行数到亿级时物理方案就会开始反超;生产高配机器(更多核、NVMe 阵列)会把交叉点大幅推后,但趋势不变:数据量越大,物理优势越大——这也是生产经验把物理迁移定位在 10TB+ 超大规模场景的原因(第 6 节)。
4.4 高写入负载下的 redo 追赶陷阱
实测中发现一个真实问题:源库高速写入时(sysbench 造数据阶段,redo 以 30MB/s+ 的速度轮转),xtrabackup 报错:
1
2
[Xtrabackup] could not find redo log file with LSN 5269084160
[Xtrabackup] read_logfile() failed.
原因:MySQL 8.0 的 redo log 是环形复用的(默认容量 100MB),写入太快时,xtrabackup 还没读到的 redo 文件已经被覆盖轮转,LSN 追丢。解决:
- 调大
innodb_redo_log_capacity(如 1G),给备份留出追赶余量 - 或避开写入高峰做备份
- XtraBackup 8.0.30+ 会提示这个错误并建议扩容 redo
这个问题在逻辑导出里不存在(SELECT 走 MVCC 快照,和 redo 无关),是物理备份特有的坑。
5. 会遇到的问题与解决方案
5.1 版本与参数强约束
物理文件迁移的兼容性约束比逻辑迁移严苛得多:
| 约束项 | 要求 |
|---|---|
| MySQL 大版本 | 8.0 的物理备份不能恢复给 5.7,反之亦然 |
| XtraBackup 版本 | 与 MySQL 版本配套(8.0.x 备 8.0.y,建议 y 不超前太多) |
innodb_page_size | 两端必须一致 |
lower_case_table_names | 两端必须一致 |
| 目标端状态 | 必须空实例(整实例替换) |
逻辑迁移(第二、三篇)几乎没有这些约束——这就是物理迁移”快”的代价。
5.2 目标端必须”空实例”
逻辑导入是往库里”加数据”,物理恢复是”换整个身体”。目标实例上已有的账号、参数、其他库,全部会被备份内容覆盖。生产操作前想清楚:目标端是新机器还是老机器复用。
5.3 prepare 不是可选项
跳过 prepare 直接启动恢复出来的数据目录 = 直接启动一个”崩溃状态”的 MySQL,轻则报错起不来,重则数据损坏。--prepare 就是让备份完成一次”受控的崩溃恢复”。
5.4 权限与路径
实测踩坑:容器化部署时,xtrabackup 找 binlog 文件用的是实例视角的路径(/var/lib/mysql),宿主机直接跑 xtrabackup 时路径对不上(报 File '/var/lib/mysql/mysql-bin.000007' not found)。解法:xtrabackup 也容器化运行,挂载相同路径。
5.5 可传输表空间的限制
- 表结构必须严格一致(.cfg 校验)
- 目标端要有同 DDL 的空表
- 不支持分区表的部分场景、外键需要
foreign_key_checks=0 - 8.0 之前跨版本传输受限(.cfg 格式不兼容)
6. 适用数据量级结论
生产经验值:XtraBackup 物理迁移的适用范围在 10TB 以上(超大数据量场景),结合前三篇形成完整的选型地图:
| 数据量级(生产经验) | 推荐方案 | 核心理由 |
|---|---|---|
| 300GB 以内 | mysqldump(调参) | 最省心,窗口能等 |
| 300GB ~ 15TB | mydumper/myloader | 逻辑迁移主力区间,多线程摊薄行级成本 |
| 10TB 以上 | XtraBackup 物理迁移 | 恢复耗时只与数据体积相关、与行数无关,数据量越大优势越大 |
| 单表级迁移 | 可传输表空间 | 单表 264MB 实测秒级导入,跳过全部 SQL 开销 |
注:10TB ~ 15TB 之间是一段重叠区——这个区间 mydumper 和 XtraBackup 都能用,按迁移窗口要求和运维熟悉度选即可。
物理迁移的三条铁律:版本/页大小/参数必须匹配、目标端必须空实例(停机替换)、prepare 一步不能省。
7. 系列总结
四篇下来,把 2000 万行 / 4.2GB 的同一份数据用三种方案各迁了一遍,全流程实测数据:
| 方案 | 全流程 | 导出冲击(源库TPS) | 适用量级(生产经验) | 上手难度 |
|---|---|---|---|---|
| mysqldump + mysql source | 632s | +12%(预热特例) | <300GB | 低 |
| mydumper + myloader | 200s | +4%~-13% | 300GB~15TB | 中 |
| xtrabackup 物理迁移 | 369s | -34% | 10TB+ | 高 |
没有银弹,四条实战心法:
- 量级定方案(生产经验):300GB 内逻辑单线程够用、15TB 内多线程逻辑最快、超大数据量物理迁移;单表迁移用可传输表空间
- 导出打哪儿很重要:主库导出冲击主库(P95 +17%),从库/ro 导出保护主库但会推高副本自身复制延迟(mysqldump 12s / mydumper 2s / xtrabackup 28s,工具越重延迟越高)——副本是否承担线上读流量决定这个 trade-off 怎么选
- 线程数有甜蜜点:4~8 线程最优,盲目加线程源库更疼、速度反而慢
- 导入调参三板斧(关 binlog、弱化刷盘、批量提交)能救 mysqldump 37%,但 myloader 默认配置已经站在调优起点上
最后说句实在的:本系列测的是”全量迁移”这一段,生产级迁移还包括增量追赶(binlog 复制)和割接(停写→追平→切流),那两步的坑不比全量少。云厂商 DTS 类产品把这些封装掉了,但理解了本系列的三种底层方案,你就知道 DTS 的”全量+增量”到底在做什么、为什么快、为什么贵。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。