如何利用MySQL可传输表空间功能高效迁移海量单表数据?
- 内容介绍
- 文章标签
- 相关推荐
本文共计980个文字,预计阅读时间需要4分钟。
确保满足以下条件,可以将`.ibd`文件直接复制:
核心前提是:源库和目标库的 MySQL 版本要一致(至少主版本相同),页大小、字符集、校对规则、行格式(ROW_FORMAT)也得一致,否则挂载会报 Tablespace mismatch 或直接崩溃。
常见踩坑点:
- 忘记在源库执行
FLUSH TABLES table_name FOR EXPORT—— 这步生成的.cfg文件含元数据校验信息,缺了就IMPORT失败 - 拷贝后没用
chown mysql:mysql修复文件属主,导致挂载时报Operation not permitted - 目标库表结构必须和源库一模一样(包括
ENGINE=InnoDB、COLLATE、STATS_PERSISTENT等隐式属性),建议用SHOW CREATE TABLE对比
迁移前必须做的三件事:锁表、生成元数据、清理脏页
不是简单停写就能操作。大表迁移最怕的是拷贝期间有 DML 写入,导致 .ibd 和 .cfg 不一致。
正确顺序是:
- 在源库执行
FLUSH TABLES table_name FOR EXPORT—— 这会加全局读锁,阻塞所有写入,并将缓冲池中该表的脏页刷盘、重置 undo log,确保.ibd是“静止快照” - 立刻复制
/var/lib/mysql/db_name/table_name.{ibd,cfg}到目标机对应路径(注意目录权限) - 在源库执行
UNLOCK TABLES解锁(FLUSH ... FOR EXPORT不自动解锁)
漏掉 UNLOCK TABLES 会导致后续所有 DDL/DML 被卡住,现象是 show processlist 里一堆 Waiting for table flush。
目标库挂载失败的典型错误和修复方法
挂载时最常见的报错是 Incorrect key file for table 或 Failed to open tablespace file,本质都是元数据不匹配。
排查优先级如下:
- 检查目标库是否已存在同名空表:
DROP TABLE IF EXISTS table_name,然后用源库SHOW CREATE TABLE重建(不能只建个空表) - 确认
.ibd文件权限为mysql:mysql,且目标目录datadir/db_name/可写 - 运行
ALTER TABLE table_name DISCARD TABLESPACE清掉残留的表空间引用(如果之前挂载失败过) - 用
hexdump -C table_name.cfg | head -20看前几字节是否为MySQLTablespace—— 如果是乱码或为空,说明.cfg没生成成功或损坏
一旦 IMPORT 成功,记得马上执行 ANALYZE TABLE table_name 更新统计信息,否则后续查询可能走错执行计划。
为什么不能直接 cp .ibd 后改名?为什么必须用 cfg
因为 InnoDB 表空间文件里不存表名、列定义、外键约束这些元数据,全靠 .cfg 提供校验和 + 字段偏移 + 主键信息。没有它,IMPORT 就像给身体换心脏却不告诉医生血型和瓣膜尺寸。
实操中有人试过删掉 .cfg 直接 IMPORT,MySQL 会报 Cannot import tablespace for table_name, no .cfg file found 并拒绝启动该表;也有人用 innodb_force_recovery 强启,但大概率触发断言失败或数据错位。
真正省时间的地方在于:整个过程不涉及 SQL 解析、日志回放、二级索引重建——拷完文件 + 几条命令,几十 GB 的表几分钟就上线。但前提是每一步都严丝合缝,少一个 FLUSH、漏一个 chown,就得从头再来。
本文共计980个文字,预计阅读时间需要4分钟。
确保满足以下条件,可以将`.ibd`文件直接复制:
核心前提是:源库和目标库的 MySQL 版本要一致(至少主版本相同),页大小、字符集、校对规则、行格式(ROW_FORMAT)也得一致,否则挂载会报 Tablespace mismatch 或直接崩溃。
常见踩坑点:
- 忘记在源库执行
FLUSH TABLES table_name FOR EXPORT—— 这步生成的.cfg文件含元数据校验信息,缺了就IMPORT失败 - 拷贝后没用
chown mysql:mysql修复文件属主,导致挂载时报Operation not permitted - 目标库表结构必须和源库一模一样(包括
ENGINE=InnoDB、COLLATE、STATS_PERSISTENT等隐式属性),建议用SHOW CREATE TABLE对比
迁移前必须做的三件事:锁表、生成元数据、清理脏页
不是简单停写就能操作。大表迁移最怕的是拷贝期间有 DML 写入,导致 .ibd 和 .cfg 不一致。
正确顺序是:
- 在源库执行
FLUSH TABLES table_name FOR EXPORT—— 这会加全局读锁,阻塞所有写入,并将缓冲池中该表的脏页刷盘、重置 undo log,确保.ibd是“静止快照” - 立刻复制
/var/lib/mysql/db_name/table_name.{ibd,cfg}到目标机对应路径(注意目录权限) - 在源库执行
UNLOCK TABLES解锁(FLUSH ... FOR EXPORT不自动解锁)
漏掉 UNLOCK TABLES 会导致后续所有 DDL/DML 被卡住,现象是 show processlist 里一堆 Waiting for table flush。
目标库挂载失败的典型错误和修复方法
挂载时最常见的报错是 Incorrect key file for table 或 Failed to open tablespace file,本质都是元数据不匹配。
排查优先级如下:
- 检查目标库是否已存在同名空表:
DROP TABLE IF EXISTS table_name,然后用源库SHOW CREATE TABLE重建(不能只建个空表) - 确认
.ibd文件权限为mysql:mysql,且目标目录datadir/db_name/可写 - 运行
ALTER TABLE table_name DISCARD TABLESPACE清掉残留的表空间引用(如果之前挂载失败过) - 用
hexdump -C table_name.cfg | head -20看前几字节是否为MySQLTablespace—— 如果是乱码或为空,说明.cfg没生成成功或损坏
一旦 IMPORT 成功,记得马上执行 ANALYZE TABLE table_name 更新统计信息,否则后续查询可能走错执行计划。
为什么不能直接 cp .ibd 后改名?为什么必须用 cfg
因为 InnoDB 表空间文件里不存表名、列定义、外键约束这些元数据,全靠 .cfg 提供校验和 + 字段偏移 + 主键信息。没有它,IMPORT 就像给身体换心脏却不告诉医生血型和瓣膜尺寸。
实操中有人试过删掉 .cfg 直接 IMPORT,MySQL 会报 Cannot import tablespace for table_name, no .cfg file found 并拒绝启动该表;也有人用 innodb_force_recovery 强启,但大概率触发断言失败或数据错位。
真正省时间的地方在于:整个过程不涉及 SQL 解析、日志回放、二级索引重建——拷完文件 + 几条命令,几十 GB 的表几分钟就上线。但前提是每一步都严丝合缝,少一个 FLUSH、漏一个 chown,就得从头再来。

