当前位置:首页 > 问答 > 正文

MySQL报错MY-012846和ER_IB_MSG_1021,远程帮忙修复故障全过程分享

根据网络技术社区分享及官方文档信息整理)

那天下午,我正在悠闲地喝着茶,手机突然像警报一样响个不停,抓起来一看,是监控系统发来的一连串报警短信,提示公司的核心业务数据库服务器出现严重故障,我心里“咯噔”一下,赶紧放下茶杯,远程连接上了那台出问题的服务器。

登录到服务器后,我首先检查MySQL的错误日志,日志文件里密密麻麻地刷着两条刺眼的错误信息,一条是“MY-012846”,另一条是“ER_IB_MSG_1021”,我记得在某个技术博客上看到过,MY-012846通常是InnoDB存储引擎层面的错误代码,而ER_IB_MSG_1021则是更具体的错误信息,我仔细往下翻看日志,在ER_IB_MSG_1021后面通常跟着一段描述,这次看到的是类似“表空间ID XX已经被丢弃”或者“试图访问一个不存在的表空间”这样的意思(根据MySQL官方文档对类似错误的解释),就是数据库的“管理员”——InnoDB引擎,在按照“地图”(数据字典)去找某个“仓库”(表对应的数据文件)时,发现这个“仓库”要么不见了,要么“地图”上标记的信息和“仓库”实际情况对不上号,导致它彻底懵了,于是罢工并疯狂报错。

业务已经中断,情况紧急,容不得慢慢研究,我首先尝试了最直接的方法——重启MySQL服务,希望这只是内存中的临时混乱,我输入了重启命令,心悬到了嗓子眼,结果令人失望,MySQL服务根本无法正常启动,错误日志里依然是那两条熟悉的错误信息在刷屏,这说明问题很顽固,是持久性的损坏,不是靠重启就能解决的。

重启大法失效,我知道必须动真格的了,根据之前看过的故障排查指南,下一步是让MySQL进入恢复模式,我修改了MySQL的配置文件,在[mysqld]段落下加了一行指令:innodb_force_recovery = 6,这个参数就像是数据库的“安全模式”,数字从1到6,级别越来越高,强制InnoDB忽略一些错误来启动服务,从而给我们一个机会把里面的数据捞出来,我直接用了最高级别6,这是最后的手段。

再次启动MySQL服务,这次,服务颤颤巍巍地起来了,虽然日志里还是有警告,但总算能连上去了,我立刻用命令行客户端连接数据库,尝试查看出问题的数据库和表,果然,在查询到某个特定的表时,客户端直接卡死或者报错,确定了“罪魁祸首”。

数据无价,我的首要任务是把还能救的数据备份出来,由于数据库处于强制恢复模式,不允许执行任何修改操作(如INSERT, UPDATE),这正好符合我的需求,我使用mysqldump工具,小心翼翼地绕开那张已经确认损坏的表,只备份其他完好的数据库和表,命令大致是这样的:mysqldump -u用户名 -p密码 --databases 数据库1 数据库2 --ignore-table=损坏数据库.损坏表名 > backup.sql,这个过程很慢,我紧盯着屏幕,生怕中途再出什么幺蛾子,谢天谢地,备份任务最终顺利完成,生成了一个宝贵的SQL备份文件。

接下来就是最决绝的一步了,我停止了MySQL服务,然后做了一个大胆的决定:直接删除了那个报错中提到的、已经损坏的表对应的.ibd数据文件(这个文件路径在错误日志里通常有提示),这就相当于把那个坏掉的“仓库”直接拆掉了,我注释掉之前加的innodb_force_recovery = 6配置行,让MySQL以正常模式启动。

MySQL服务再次启动,这一次,启动过程异常顺利,错误日志清静了,我登录进去,发现那个损坏的表虽然还在,但数据已经没了,因为它对应的数据文件被我删除了,这正在我意料之中,我直接删掉了这个表的空壳(DROP TABLE),然后从刚才备份的SQL文件中,找到创建表结构的语句,重新创建了这张表,幸运的是,这张表里的数据并非不可替代,可以从其他途径部分恢复或重新生成,如果是核心数据,那我就得去尝试用更专业的工具(如Percona的恢复工具)去解析那个损坏的.ibd文件了,那将是另一个更复杂的故事。

处理完后,我再次全面检查了数据库的状态,确保没有其他隐患,复盘这次故障,根源很可能是之前服务器非正常关机(比如突然断电)导致InnoDB的元数据(就是那张“地图”)和实际的数据文件出现了不一致,这次经历让我再次深刻体会到,定期备份并验证备份的有效性,是多么重要的一条生命线,整个远程修复过程,从报警到最终恢复,花了将近三个小时,虽然紧张,但步骤清晰,总算是有惊无险。

MySQL报错MY-012846和ER_IB_MSG_1021,远程帮忙修复故障全过程分享