根本原因是.ibd文件与内存表定义不匹配,需同时满足:用FLUSH TABLES...FOR EXPORT导出(含元数据校验)且目标表结构完全一致(包括ROW_FORMAT、字符集、列序等)。

为什么 ALTER TABLE ... IMPORT TABLESPACE 总报错 “Tablespace is missing for table”
根本原因不是文件丢了,而是 MySQL 没法把 .ibd 文件和内存中的表定义对上号。它需要两个前提同时满足:.ibd 文件必须是用 FLUSH TABLES ... FOR EXPORT 生成的(带元数据校验),且目标表必须先用 CREATE TABLE 建好、结构完全一致(包括 ROW_FORMAT、KEY_BLOCK_SIZE、字符集、列顺序、隐藏字段等)。
常见错误现象:
ERROR 1808 (HY000): Schema mismatch (Table has ROW_FORMAT=COMPACT, but the tablespace file has ROW_FORMAT=Dynamic)-
ERROR 1812 (HY000): Tablespace is missing for table `db`.`t`(其实是表定义没加载成功,不是文件路径问题) - 导入后查不到数据,或
SELECT报ERROR 2013 (HY000): Lost connection
实操建议:
- 源库导出前务必执行
FLUSH TABLES `t` FOR EXPORT,完成后立刻COPYt.ibd和t.cfg(不能只拷.ibd) - 目标库建表语句必须用
SHOW CREATE TABLE从源库原样复制,不能手工重写——尤其注意ENGINE=InnoDB后面有没有ROW_FORMAT=、STATS_PERSISTENT=等隐式参数 - 导入前删掉目标表的
.ibd,但保留.frm(MySQL 5.7)或数据字典记录(MySQL 8.0),再执行ALTER TABLE t DISCARD TABLESPACE
MySQL 8.0 下 IMPORT TABLESPACE 失败:.cfg 文件还用不用?
8.0 之后 InnoDB 不再依赖 .cfg 文件做校验,但你仍得提供它——否则会报 ERROR 1812: Tablespace file not found。这不是 bug,是设计行为:MySQL 8.0 仍会读取 .cfg 中的 space_id 和 page_no 校验信息,只是不再用它恢复列定义。
使用场景:
- 跨实例迁移单表(比如从测试库导出,导入到生产库隔离环境)
- 误删表后,有冷备份的
.ibd+.cfg,且能重建同结构表
实操建议:
- 即使用了
innodb_file_per_table=ON,也别直接 cp.ibd过去就 import——少了.cfg或版本不匹配,99% 失败 - MySQL 8.0.23+ 支持
DATA DIRECTORY子句,可指定.ibd物理位置,但.cfg必须放在同一目录,且文件名严格为t.cfg - 如果只有
.ibd没有.cfg(比如从崩溃实例抢救出来的文件),只能用innodb_force_recovery+mysqldump导出,不能走 transport
transportable tablespace 能否跳过 DISCARD 直接替换 .ibd?
不能。MySQL 强制要求先 DISCARD TABLESPACE,再 COPY 文件,最后 IMPORT。这是为了确保内存中表对象和磁盘文件状态同步。跳过 DISCARD 直接覆盖 .ibd,会导致 buffer pool 缓存和文件页不一致,轻则查询返回脏数据,重则 mysqld crash。
性能与兼容性影响:
- 整个过程会锁表(
FLUSH TABLES ... FOR EXPORT和IMPORT阶段都需独占 MDL 锁),不适合大表在线迁移 - 不同 MySQL 小版本间
.ibd格式可能不兼容(比如 5.7.30 导出的文件,在 5.7.40 导入失败),建议主版本一致,次版本尽量接近 - 如果表启用了加密(
ENCRYPTION='Y'),源库和目标库的 keyring 插件配置必须完全一致,否则IMPORT时解密失败
实操建议:
- 不要在业务高峰期执行;大表(>10GB)建议先在从库验证流程
- 导入后立即执行
ANALYZE TABLE t,避免优化器用旧统计信息导致执行计划劣化 - 确认
innodb_page_size在两端一致(默认 16KB),否则.ibd无法识别
恢复后 SELECT 返回空或报错:如何快速定位 tablespace 是否真正生效?
最直接的办法是查 INFORMATION_SCHEMA.INNODB_TABLESPACES 和 INNODB_SYS_TABLES,看 SPACE 字段是否非零且与 .ibd 文件头一致。但更实用的是用 mysqlcheck 或手动触发一次 buffer pool 加载。
常见错误现象:
-
SELECT COUNT(*) FROM t返回 0,但SHOW TABLE STATUS显示Rows > 0 -
ERROR 1030 (HY000): Got error 168 from storage engine(通常是 page corruption 或 space_id 错配)
实操建议:
- 运行
SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME = 'db/t',检查SPACE值是否大于 0;再用hexdump -C t.ibd | head -20查文件头第 12–15 字节,对比是否一致 - 执行
SELECT * FROM t LIMIT 1后立刻查SHOW ENGINE INNODB STATUS\G,在TRANSACTIONS部分看是否有lock wait,在FILE I/O看是否有pending normal aio reads卡住 - 如果怀疑文件损坏,用
innochecksum -v t.ibd(MySQL 5.7)或mysqld --innodb-checksum-algorithm=crc32 --innodb-force-recovery=1启动验证
真正麻烦的不是步骤多,是每个环节的校验点都藏在不同地方:.cfg 里埋着 space_id,.ibd 头里藏着 page_no,数据字典里记着 row_format——少对上一个,就静默失败。


















