mysql如何排查InnoDB页损坏_mysql innodb_force_recovery模式

2次阅读

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

mysql如何排查InnoDB页损坏_mysql innodb_force_recovery模式

看到 InnoDB 启动失败或报 tablespace is corrupted 怎么办

别急着删数据目录或重装 mysql,先确认是不是真损坏、损在哪。InnoDB 的页损坏通常表现为服务无法启动、查询卡死、日志里反复出现 ibuf_merge_or_delete_for_pagefil_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 出来再导入新实例就以为万事大吉,结果发现某些字段值全变成 0NULL,或者时间字段乱成 1970-01-01 —— 这往往不是 dump 问题,而是原表本身已有逻辑损坏,而 innodb_force_recovery 在跳过恢复时没校验数据页内容,直接把脏数据读出来了。

  • 导出前务必检查 INFORMATION_SCHEMA.INNODB_SYS_TABLESINNODB_SYS_INDEXES,看对应表的 space 值是否异常(比如为 0 或负数)
  • 导出时加 --skip-triggers --skip-routines --skip-events,避免触发器/存储过程依赖已损坏的元数据导致中断
  • 导入后用 CHECK TABLE 对每张表做校验,尤其注意 data_free 是否异常偏大,或 Rows_examined 在简单查询中飙升

为什么不用 mysqlcheck --repairinnochecksum

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_logfile0ib_logfile1 再试。

text=ZqhQzanResources