背景

在使用 InnoDB 存储引擎的 MySQL 环境中, innodb_force_recovery 是排障过程中十分关键的参数:

  1. 对于业务连续性要求极高的场景, 长时间停机往往难以接受. 适当设置 innodb_force_recovery 能够帮助实例快速恢复, 临时维持服务能力.
  2. 当遭遇严重的 MySQL 内核 Bug, 例如数据页 (Page) 或 Undo Log 损坏时, 常规的 crash recovery 可能无法完成, 导致实例无法启动. 此时借助该参数可以让数据库以“带病”模式运行.
  3. innodb_force_recovery 的取值范围为 0-6 (默认 0), 数值越大代表跳过的恢复步骤越多, 高级别也包含低级别的功能. 例如设置为 1 表示跳过损坏数据页, 设置为 2 则在此基础上阻止部分后台线程 (如 master thread 与 purge thread) 启动.

参数 innodb_force_recovery

参数 innodb_force_recovery 的使用方式非常直接, 在配置文件中新增或调整该参数即可:

1
2
[mysqld]
innodb_force_recovery = 1

在 MySQL 8.0 版本中, 只要 innodb_force_recovery 大于 0, 所有已有表的 ALTER TABLE 等 DDL 操作都会被禁止:

1
ERROR 1881 (HY000): Operation not allowed when innodb_force_recovery > 0.
  • 当 innodb_force_recovery 小于等于 3 时, 仍允许执行 DROP TABLE 与 CREATE TABLE.
  • 当 innodb_force_recovery 小于 4 时, DROP TABLE 操作仍可执行.
  • 当该参数设置为 4 或更高值时, InnoDB 会被迫进入只读模式, 包括 DROP TABLE、CREATE TABLE 在内的所有 DDL 操作都会被拒绝.

6 个恢复级别详解

innodb_force_recovery = 0

  • 作用: 正常模式, 不进行强制恢复.
  • 使用场景: 默认值, 正常运行时使用.
  • 风险: 无风险.
  • 注意事项: 无.

innodb_force_recovery = 1 (SRV_FORCE_IGNORE_CORRUPT)

  • 作用: 在 crash recovery 阶段不做额外处理, 数据库启动后跳过损坏的数据页.
  • 使用场景: 适用于检测到个别数据页损坏、但整体表空间仍可访问的场景.
  • 风险: 逻辑上依赖被跳过数据页的业务可能报错或返回不完整数据.

innodb_force_recovery = 2 (SRV_FORCE_NO_BACKGROUND)

  • 作用: 阻止后台线程启动, 包括:
    • master 线程
    • undo log purge 线程
  • 使用场景: undo log purge 过程中频繁崩溃, 或后台线程运行导致实例无法恢复.
  • 风险: undo log 堆积, 长期运行会造成空间占用与性能下降.

innodb_force_recovery = 3 (SRV_FORCE_NO_TRX_UNDO)

  • 作用: 禁止执行事务回滚, 忽略未提交事务对应的 undo log.
  • 使用场景: 在事务回滚阶段反复崩溃、或 undo log 本身存在损坏时使用.
  • 风险: 未提交事务无法回滚, 相关表数据可能出现不一致或脏读.

innodb_force_recovery = 4 (SRV_FORCE_NO_IBUF_MERGE)

  • 作用:
    • 阻止 ibuf 合并操作
    • 将 InnoDB 强制切换到只读模式
  • 使用场景: ibuf 合并过程中实例崩溃, 或二级索引页损坏.
  • 风险:
    • 所有写入被禁止, 只能导出数据.
    • 二级索引可能不完整, 后续需要全部重建以消除隐患.

innodb_force_recovery = 5 (SRV_FORCE_NO_UNDO_LOG_SCAN)

  • 作用:
    • 启动时跳过 undo log 扫描, 未提交事务在启动后会被视为已经提交.
    • 在 crash 前尚未完成的 DDL 不再回滚; 自 MySQL 8.0 起 DDL 为原子操作, 但此级别可能遗留临时文件.
    • InnoDB 维持只读状态.
  • 使用场景: undo log 文件损坏或扫描 undo log 会触发崩溃.
  • 风险: 数据可能出现严重不一致, 需尽快导出并校验.
  • 注意事项: 启动后请尽量只做只读操作, 立即备份关键数据.

innodb_force_recovery = 6 (SRV_FORCE_NO_LOG_REDO)

  • 作用: 阻止 redo log 前滚, 在 crash recovery 阶段几乎跳过所有操作.
  • 使用场景: 当 1-5 级别均无法启动实例时的最后尝试, 仅用于导出数据.
  • 风险: 已提交但未刷盘的事务全部丢失且不可恢复, 数据完整性无法保证.
  • 注意事项: 启动后立即导出数据并计划重建实例, 切勿继续在该实例上承载业务.

innodb_force_recovery 的使用策略应当以 1 为起点逐级调高, 或在明确问题根源的情况下直接选择对应级别来跳过相关步骤.

数据恢复

无论 innodb_force_recovery 设置为何值, 它都无法真正修复数据, 只是暂时绕过错误让实例得以启动. 一旦确认存在数据损坏或其他严重内核 Bug, 在成功拉起实例后仍需第一时间完成数据恢复与校验.

使用 mysqldump 工具备份数据

在启用了 innodb_force_recovery 的实例上建议尽量不要进行写操作, 应第一时间完成逻辑备份, 以免损坏进一步扩散。最常见的做法是借助 mysqldump 导出关键库表。可以先运行以下命令快速获取指定库的数据:

1
mysqldump --single-transaction --databases db_name > db_name.sql

常用参数说明如下:

  • mysqldump: MySQL 自带的逻辑备份工具, 会以 SQL 的形式导出表结构与数据.
  • --single-transaction: 在同一个事务里读取所有表, 保证逻辑一致性 (仅适用于 InnoDB).
  • --databases db_name: 指定要导出的数据库, 可重复多次; 若要导出单表可使用 db_name tbl_name 的形式.
  • > db_name.sql: 将导出结果重定向到文件, 便于后续数据恢复.

导出完成后, 建议先将疑似损坏的表重命名以免覆盖原始文件, 然后再导入备份:

1
2
3
-- 在 mysql 客户端中执行
RENAME TABLE db_name.tbl_name TO db_name.tbl_name_bak;
SOURCE /path/to/db_name.sql;

如此一来可以保留损坏前的数据文件作为对照, 并通过 SOURCE 命令直接回放刚才导出的 SQL 脚本, 快速恢复表结构与数据。

InnoDB 表检查

无论是在备份之前还是恢复之后, 都可以通过 CHECK TABLE 语句确认表空间是否损坏. 具体语法可参考官方文档: https://dev.mysql.com/doc/refman/8.0/en/check-table.html

1
CHECK TABLE tbl_name;
  1. 在执行 CHECK TABLE 之后, 建议对输出结果逐项评估。
  2. 如果发现 status 列返回 corrupt, 应结合备份结果、二进制日志或从库数据进行修复与比对, 必要时重建表结构或回放备份, 确保恢复后的数据一致性。
  3. 需要注意的是, CHECK TABLE 主要针对页级与索引结构的物理损坏, 对于由业务错误、未提交事务或应用层造成的逻辑不一致无能为力。它也不会检查 .ibd 文件中的空间 ID、索引 ID 等元信息是否与数据字典匹配, 因此面对手动拷贝表空间文件或元数据错位问题时难以及时发出告警。
  4. 同时在大表上执行检查时可能会触发表锁或额外 I/O, 不宜在高峰期频繁运行。