innodb表空间损坏需先区分物理页损坏与文件丢失:报“corrupted page found”等属物理损坏,用innodb_force_recovery逐级(1-4)只读导出数据;报“tablespace is missing”则为文件丢失或权限问题,应检查.ibd是否存在及权限。

InnoDB表空间损坏不能“修复”,只能“抢救数据”——innodb_force_recovery不是修复开关,而是只读逃生通道。
怎么确认是InnoDB表空间损坏,而不是文件丢失或权限问题
先别急着改配置。错误日志里出现InnoDB: Database page corruption、Corrupted page found或Failed to read page,基本锁定物理页损坏;若报Tablespace is missing for table或Operating system error number 2,大概率是.ibd文件被删、移动或chmod 000了。这时候SHOW ENGINE INNODB STATUS\G里的LATEST DETECTED CORRUPTION段比SHOW CREATE TABLE更可信——它会明确指出出问题的表名和文件路径。
顺手跑一遍:mysqlcheck --check --extended --all-databases,看是否只有个别表报error: record is corrupted。如果全库大面积报错,优先怀疑ibdata1或系统表空间元数据损坏,而不是单个.ibd。
innodb_force_recovery该设几?从1开始试,别跳
这个参数不是越大越好,也不是设了就能导出全部数据。它从1到6是逐级放宽崩溃恢复限制,但每升一级,跳过的结构就越多:
-
innodb_force_recovery = 1:跳过崩溃回滚段,适合事务卡死、锁表无法SELECT的场景;此时仍允许INSERT/UPDATE(但不推荐) -
innodb_force_recovery = 2:停掉purge线程和change buffer合并,避免后台写入干扰;这时只允许SELECT,禁止写操作 -
innodb_force_recovery = 3:忽略未提交事务,ACID保障失效,但能绕过事务回滚失败导致的启动卡死 -
innodb_force_recovery = 4及以上:禁用插入缓冲、清除线程甚至重做日志应用,极易引发二次崩溃,生产环境慎用
实操建议:
- 每次只设一个值,改完
my.cnf必须重启mysqld,不能热加载 - 启动成功后立刻执行
mysqldump --lock-tables=false --routines --triggers database_name > backup.sql(--single-transaction在recovery模式下常失效) - 若导出卡在某张表,降低级别重试;别硬扛——卡住说明当前级别已跳过关键结构,再等也没用
导出后必须立刻关掉innodb_force_recovery
这个参数一旦启用,MySQL会拒绝所有写操作(包括DROP、ALTER、CREATE),长期开着等于把数据库“钉”在只读状态。导出完成后的第一件事,是注释掉my.cnf里的innodb_force_recovery行,然后重启服务。
常见疏漏:
- 导出后忘了注释配置,结果第二天发现业务写不进数据
- 误以为“能SELECT就代表修好了”,没验证导出SQL里是否漏掉部分记录(特别是损坏页附近的行)
- 没核对
backup.sql末尾有没有ERROR或Warning,导致导入时才发现主键冲突或字段截断
单个.ibd文件损坏且innodb_file_per_table=ON时,可重建文件
前提是:你有该表的建表语句(或MySQL 8.0+的数据字典)、.frm文件(5.7及以前)没丢,并确认ibdata1和数据字典本身完好。流程是:
- 用
CREATE TABLE t1_new LIKE t1;建空表 -
ALTER TABLE t1_new DISCARD TABLESPACE;删掉新表的.ibd - 把原损坏的
t1.ibd拷贝过去,改名为t1_new.ibd -
ALTER TABLE t1_new IMPORT TABLESPACE;——这步会校验页校验和,失败说明损坏太深,得回退到force_recovery导出
注意:IMPORT TABLESPACE要求ibdata1里对应的数据字典记录还存在,且t1.ibd的space_id必须和字典一致;用hexdump -C t1.ibd | head -20能查到前几个字节的space_id,不匹配会直接报错。
真正麻烦的从来不是参数怎么设,而是损坏页的位置不确定、导出时跳过哪几行没人知道——所以每次修改innodb_force_recovery前,先用SELECT COUNT(*) FROM t1;和SELECT COUNT(*) FROM t1 USE INDEX (PRIMARY);交叉对比,能快速暴露是否漏读了索引或聚簇页。











