我干数据库运维这行快十年了,最怕半夜手机响。上周三凌晨两点,电话那头是刚入职三个月的小伙子,声音都在抖:“哥,表没了,整张user表,drop了。”我心想完了,这又是哪个测试库手滑点到生产库。结果他补了一句:“没开binlog,备份是上周的。”听到这儿我反倒不慌了,因为我知道,这活儿还有救。

很多人一听表被drop,第一反应就是翻备份、找binlog,要是这两样都不给力,就直接放弃治疗。但实际上,MySQL在Linux服务器上删掉表,只要磁盘空间没被后续数据覆盖,表结构定义和索引页大概率还躺在原来的数据文件里。这时候拼的就是一个“快”字,越快操作,恢复概率越高。
第一步,别慌,先冻结一切写操作。你可能会觉得这不废话吗,但真出事的时候,很多人第一反应是重启MySQL、跑repair、甚至重新建表,这些操作都在往磁盘上写数据,等于亲手把还能救的数据抹掉。正确的做法是立刻把MySQL实例设为只读,或者直接停掉应用连接,然后确认一下磁盘挂载状态。如果表是独立表空间(innodbfileper_table=ON),那对应的.ibd文件路径还在,只是被标记删除了。
第二步,用系统工具把“已删除但未覆盖”的磁盘块捞回来。这一步是核心,工具就两样:lsof和debugfs。先跑lsof | grep deleted,看MySQL进程还抓着哪些已删除文件的句柄。如果进程没重启,句柄还在,那太好了,直接cp /proc/{pid}/fd/{fd号} /恢复目录/原表名.ibd,文件就回来了。如果进程已经重启过,句柄丢了,那就得上debugfs,进入文件系统底层,找到那块的inode记录,手动把数据导出来。这活儿有点糙,但实用。
第三步,把捞回来的.ibd文件重新挂回MySQL。表结构定义(.frm或数据字典里的元数据)可能还在,也可能没了。如果表结构还在,直接建一个同名的空表,然后DISCARD TABLESPACE,再把恢复出来的.ibd拷进去,执行IMPORT TABLESPACE,数据就回来了。如果表结构文件也没了,那麻烦一点,你得用strings从.ibd文件里把列名和类型抠出来,重建表结构,然后再导入。这一步考验功力,但绝大多数情况下,只要.ibd文件完整,表结构都能拼出来。
这套流程走下来,成功率大概能到七成以上,前提是你动作够快,而且磁盘没被大量写入。上周那个小伙子运气不错,他那个测试库平时没人用,磁盘上没多少新数据,我远程指挥他一步步操作,四十分钟就把表捞回来了。他后来跟我说,那四十分钟感觉比四年还长。
不过话又说回来,这招只能算“急救”,不能当常规方案用。我见过太多团队,出事前觉得备份无所谓,出事后靠这套野路子捞数据,捞回来就继续裸奔,结果下次运气不好就真没了。真正的稳妥做法,还是把binlog打开,定期全备加增量备份,再搞个延迟从库兜底。但你要是真遇到紧急情况,又没这些保障,那这三步就是你的救命稻草。
提醒一句,恢复出来的数据一定要做完整性校验。拿恢复的表和业务日志对一对,看看最近几小时的数据有没有缺漏,因为被drop的表可能还涉及未提交事务,或者删除后又有部分页被覆盖。宁可多花半小时校验,也别上线后才发现数据对不上,那就真没法跟业务交代了。
这事儿过去一周了,那个小伙子现在见人就讲这三步,还主动给团队写了套应急手册。其实数据库这东西,平时看着稳如老狗,真出事就是生死时速。你手里的三板斧,可能决定了明天是写复盘报告还是正常下班。保存好这套方法,关键时刻能救命。


