凌晨两点,电话铃声炸响。那头的兄弟声音都在抖:“库挂了,用户表全红了,老板明天一早要看数据。”我揉着眼睛问他,有没有备份。沉默了三秒,他说有,但备份是三天前的。这种场景我见过太多次了,每次都有人觉得MySQL稳如老狗,直到InnoDB在你面前表演原地去世。其实MySQL表损坏没那么玄乎,大部分情况下都能救回来,关键是别慌,也别乱动,按步骤来,三天前的备份和当前的数据之间,那点落差完全能补上。

第一步,先搞清楚到底坏成什么样了。很多人一听到表损坏就急着跑修复工具,这是最大的忌讳。你先用命令行登录MySQL,对那个有问题的表跑一句。它会告诉你状态是OK、ERROR还是CRASHED。同时看一眼错误日志,通常在或者下面,找找有没有或者这种字眼。这一步的核心目的,是判断损坏范围——是单个索引页坏了,还是整个表空间文件出了问题。我见过最典型的案例,就是有人慌里慌张直接跑,结果把本来只坏了一个索引的表,给整成了彻底打不开。所以记住,先诊断,再动手,这跟去医院一个道理,什么都没查清楚就开刀,那不是救人,是害人。
第二步,如果诊断结果是表结构还在,但数据页读不出来,优先尝试强制导出。很多人不知道,其实能跳过损坏的行继续导。你可以在命令行里加上参数,它会在遇到错误时打印警告,但不中断整个导出过程。我建议你同时加上和,前者保证一致性快照,后者避免把整个结果集加载到内存里。这一步的操作逻辑很简单——用工具把能读出来的数据先抢救出来,那些读不出来的坏行,记录下来,后面单独处理。有一次我帮一个客户导数据,一张五十万行的订单表坏了四万多行,用硬是导出了四十五万行,剩下的坏行从binlog里重新补回去,最终一张表的数据完整度达到了百分之九十九点九。你可能会问,那百分之零点一呢?是客户自己删的,跟损坏没关系。
第三步,也是最容易被人忽略的一步,利用binlog回放找回损坏行。MySQL的binlog就像飞机的黑匣子,只要开启了参数,所有修改操作都有记录。你先把损坏的表现在导出的数据导入到一个新建的表里,然后从上次完整备份的时间点开始,回放binlog。具体操作是:先用把binlog文件转换成可读的SQL文本,然后用或者过滤出针对那张表的、、语句,导入到新表里。这里有个细节,binlog回放时最好加上参数,避免二次写入损坏。去年有个电商客户,商品表损坏后三天内的新订单全丢了,就是用这个方法,从两个binlog文件里捞回了四百多笔交易,一分钱没少。当然,前提是你得开着binlog,如果当初图省事没开,那这招就只能干瞪眼。
说到备份,我必须提醒你一句,备份不是复制几个文件就完事。物理备份(直接把文件拷走)和逻辑备份(用导出SQL)各有优缺点。物理备份恢复快,但必须跟binlog配合才能保证一致性;逻辑备份灵活,能按表恢复,但大表恢复起来慢得让人崩溃。我见过最惨的案例,是有人用云服务商的快照做备份,结果快照本身是跟业务高峰期同时段的,恢复出来的数据全是半截子。所以备份策略至少要分三层:一天一次全量逻辑备份,两小时一次增量binlog备份,再加上实时主从复制。三层都做好,你才能拍着胸脯说,表坏了不怕。
还有一个容易踩的坑,内存和磁盘的隐性故障。很多表损坏其实是硬件问题引起的,比如内存条某个位翻转,或者磁盘坏道。如果你只是把表修复好了,但硬件问题没解决,那过几天还会坏,而且可能坏得更严重。我建议你在修复完数据后,立刻检查日志,看看有没有或者的记录。如果有,赶紧跟运维申请换硬件,别舍不得。去年有个朋友,一个月内同一张表坏了三次,每次都是修复完就好了,但隔几天又坏。查出来是那块SSD的固件bug,换了块盘之后,再没出过问题。数据恢复是治标,硬件排查才是治本。
说一句,如果你试了上面三步还是救不回来,别死磕。把损坏的表文件(和)原封不动地打包存好,联系专业的数据恢复公司。他们有专门的工具和脱机环境,能处理更底层的损坏。但价格不便宜,一次几千到几万都有可能。所以,回到开头那个凌晨两点的电话,我问那兄弟一句话:你那个三天前的备份,加上binlog,能拼出完整数据吗?他查了一下,binlog是开着的,那一刻他的声音才稳下来。MySQL表损坏不可怕,可怕的是你连binlog都没开,还觉得自己稳如狗。三步走下来,大部分数据都能保住,但最稳的那一步,永远是你在出事之前就做好的备份。


