MariaDB断电致InnoDB表损坏 强制恢复模式下修复索引失败
MariaDB InnoDB损坏表/索引数据恢复方案
前置必做操作(防止二次损坏)
- 立刻停止MariaDB服务,将默认路径为
/var/lib/mysql的整个数据目录完整拷贝到独立存储磁盘做冷备份,后续所有恢复操作都在备份副本上验证,原始损坏目录全程留底不要修改。 - 保留配置文件中
innodb_force_recovery=3的设置,不要贸然将该参数调整到4及以上,参数值越高触发不可逆数据丢失的概率越大,3级别仅支持只读查询、不执行后台回滚操作,是当前导出数据的最优配置。
第一步:绕过损坏索引导出全量可读取数据
你当前遇到的mysqldump索引报错、mariadb-check触发服务崩溃,本质是操作触发了损坏的二级索引、跨坏页读取逻辑,直接走主键聚簇索引读取即可避开大部分崩溃问题:
- 先导出所有无报错的正常表,导出时跳过已经发现问题的表,命令如下:
mysqldump -u root -p --default-character-set=utf8mb4 --force --skip-lock-tables --quick --ignore-table=homeassistant.statistics --ignore-table=homeassistant.states --databases homeassistant > normal_tables_backup.sql
参数说明:--force表示碰到报错自动跳过继续导出,--quick表示逐行读取结果不缓存到内存,避免大表导出触发内存溢出,--skip-lock-tables适配强制恢复模式下的锁限制。如果导出其他表时也碰到同类索引损坏报错,把对应表名加到--ignore-table参数列表里,后续单独处理。
- 针对已经报错的问题表,不要用整表dump命令,直接按主键范围分段读取数据,全程不触碰损坏的二级索引:
- 登录MariaDB查询问题表的主键字段,通常是自增
id字段:
DESCRIBE statistics; DESCRIBE states;- 按主键区间分段导出数据,每次导出1000-5000行,碰到坏块读报错就直接跳过对应区间,避免触发服务崩溃,示例语句(假设主键字段为
id):
逐段调整主键区间范围,直到把所有能正常读取的行都导出为csv格式文件。SELECT * FROM statistics WHERE id BETWEEN 0 AND 5000 INTO OUTFILE '/tmp/statistics_0_5000.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n'; - 登录MariaDB查询问题表的主键字段,通常是自增
注意:这个阶段绝对不要执行
REPAIR TABLE、OPTIMIZE TABLE、mariadb-check --repair这类写入操作,在强制恢复模式下跑这类命令大概率会直接把坏表彻底写废,没有恢复可能。
第二步:重建实例导入恢复数据
- 等所有可读取的数据都导出完成后,停止MariaDB服务,把损坏的旧数据目录整个移走,初始化一个全新的干净MariaDB实例。
- 先把第一步导出的
normal_tables_backup.sql导入新实例,确认所有正常表数据完整可用。 - 在新实例中手动创建和原表结构完全一致的问题表,建表时只保留主键约束,暂时不要创建任何二级索引,再把之前导出的csv格式数据逐批导入对应表中。
- 所有数据行导入完成后,再手动给表加回原来的二级索引。加索引过程中如果某段数据报错,就定位到对应的残缺损坏行删除,直到所有索引创建完成。
极端情况兜底方案
如果按主键分段读取还是频繁触发服务崩溃、无法读取数据,可以将innodb_force_recovery参数从3开始逐次加1,每次调整后重启服务尝试读取数据,参数最高不要调到6以上。6级别会强制跳过所有损坏页,能读取多少数据算多少,导完数据必须立刻重建实例,绝对不能在innodb_force_recovery大于0的实例上运行业务。
如果碰到表结构都无法读取的极端情况,可以从冷备的ibd文件中通过页扫描工具提取记录,这类操作复杂度较高,前面步骤能正常走完的话不需要使用。
内容的提问来源于stack exchange,提问作者Mazzy
相关产品推荐
相关产品推荐

