innodb表空间损坏时,应先备份datadir,再用innodb_force_recovery=1~6逐级尝试启动并导出数据;值越大跳过恢复步骤越多但写操作限制越严,导出后需校验数据完整性。

看到 InnoDB 启动失败或报 Tablespace is corrupted 怎么办
别急着删数据目录或重装 MySQL,先确认是不是真损坏、损在哪。InnoDB 的页损坏通常表现为服务无法启动、查询卡死、日志里反复出现 ibuf_merge_or_delete_for_page 或 fil_io 相关错误,或者 SHOW ENGINE INNODB STATUS 里有 corrupted 字样。
真正要做的第一步,是停掉 MySQL,备份整个 datadir(哪怕只是 cp -r 一份),再尝试用 innodb_force_recovery 启动——它不是修复工具,而是“绕过损坏页,把还能读的数据捞出来”的逃生开关。
-
innodb_force_recovery只在 MySQL 配置文件(如/etc/my.cnf)的[mysqld]段里生效,值为 1–6,数值越大跳过的恢复步骤越多,但写操作越受限 - 从
1开始试,每次改完必须重启 mysqld;一旦能连上,立刻用mysqldump导出所有可用库表,不要等、不要查原因 - 值为
4及以上时,INSERT/UPDATE/DELETE全部被禁用,连DROP TABLE都会报错,只能读和导出
innodb_force_recovery=1 到 =6 各级实际影响差异
这六个级别不是线性增强,而是按 InnoDB 恢复流程的关键节点逐层跳过。比如 =1 只跳过回滚段崩溃恢复,而 =3 已经跳过插入缓冲合并——后者一跳,就可能让后续查询因索引不一致而报错,但至少能连上。
-
=1:跳过回滚段恢复 → 能启动,但未提交事务可能丢失,SELECT大概率正常 -
=2:再跳过主线程崩溃恢复 → 防止因后台线程异常卡住启动 -
=3:跳过插入缓冲合并 → 索引页可能不完整,SELECT可能报Record not found in index,但多数表仍可导出 -
=4:跳过 ibuf 和 undo 日志 → 所有写操作禁止,mysqldump是唯一安全出口 -
=5和=6:进一步跳过事务系统初始化和回滚段加载 → 极端情况才用,连SHOW TABLES都可能失败,慎用
导出后重建库时容易忽略的两个硬伤
很多人 dump 出来再导入新实例就以为万事大吉,结果发现某些字段值全变成 0 或 NULL,或者时间字段乱成 1970-01-01 —— 这往往不是 dump 问题,而是原表本身已有逻辑损坏,而 innodb_force_recovery 在跳过恢复时没校验数据页内容,直接把脏数据读出来了。
- 导出前务必检查
INFORMATION_SCHEMA.INNODB_SYS_TABLES和INNODB_SYS_INDEXES,看对应表的space值是否异常(比如为0或负数) - 导出时加
--skip-triggers --skip-routines --skip-events,避免触发器/存储过程依赖已损坏的元数据导致中断 - 导入后用
CHECK TABLE对每张表做校验,尤其注意data_free是否异常偏大,或Rows_examined在简单查询中飙升
为什么不用 mysqlcheck --repair 或 innochecksum
mysqlcheck --repair 对 InnoDB 表完全无效,它只作用于 MyISAM;innochecksum 是个只读校验工具,它能告诉你某页 CRC 不对,但不能定位哪条记录坏、也不能跳过它继续读——你还是得靠 innodb_force_recovery 启动后人工判断哪些表能救、哪些该舍。
-
innochecksum -v /var/lib/mysql/test/t1.ibd可以扫出损坏页号,但页号和实际行记录无直接映射,对恢复帮助极小 - 试图用
innodb_force_recovery=6强启然后ALTER TABLE ... ENGINE=InnoDB重建?大概率失败,因为 DDL 本身需要事务支持,而=6下事务系统已不工作 - 真正可靠的兜底动作只有两个:从最近可用备份恢复,或从
innodb_force_recovery启动后的 dump 中抢救尽可能多的有效数据
innodb_force_recovery 后没清空 ib_logfile* 就重启,导致 MySQL 因日志不匹配再次拒绝启动——遇到这种情况,删掉 ib_logfile0 和 ib_logfile1 再试。










