前几天一个朋友半夜给我打电话,声音都带着哭腔,说他手滑在服务器上执行了drop database,整个项目数据库瞬间消失。这种事在程序员圈子里太常见了,我见过太多人因为一个回车键整夜睡不着觉。MySQL删库之后到底能不能恢复,这个问题其实没有标准答案,完全取决于你之前做了多少准备。很多人觉得数据库备份是运维的事,自己只管写代码,结果出事才发现连个备份脚本都没写过。现实就是这么残酷,恢复数据的难度和你备份的完整程度直接挂钩,没有任何捷径可走。

如果你运气好,MySQL开启了binlog,那就能恢复。binlog是MySQL的二进制日志,记录着所有数据变更操作,包括那个要命的drop database。默认情况下,大多数生产环境都会开启binlog,但很多人的开发机或者测试环境压根没开。你得先用SHOW VARIABLES LIKE 'log_bin'确认一下。如果返回的是ON,恭喜你,还有戏。接下来要找到删除操作之前那个时间点的binlog文件,然后用mysqlbinlog工具把日志导出成SQL。关键是别直接把整个binlog导出来执行,那样会把删库之后的操作也重放一遍,反而把数据搞乱。正确的做法是在导出的SQL里找到drop database那行,直接删掉,再执行剩下的内容。这个过程很考验耐心,因为binlog文件可能很大,你得一点一点排查时间点。
但很多人遇到的问题是,binlog开了但没设置过期时间,导致日志文件太多太大,根本找不到目标时间点。这时候就得靠系统里还保留的物理文件了。MySQL的数据默认存在/var/lib/mysql目录下,每个数据库对应一个子目录。如果你执行的是drop database,系统会直接删除这个目录和里面的所有文件。但注意,Linux系统下文件被删除后,只要进程还在运行,文件句柄就没释放,数据其实还在磁盘上。你可以立刻停止MySQL服务,然后使用extundelete这类工具去恢复被删除的文件。这个操作要快,因为磁盘空间随时可能被新数据覆盖。恢复出来的文件复制到安全位置,再重新挂载到MySQL里,运气好的话能找回大部分数据。
还有种情况特别坑,就是用了InnoDB引擎但没有开独立表空间。InnoDB默认把所有数据存在一个共享表空间文件ibdata1里。如果你执行了drop database,InnoDB会在内部标记这些数据为已删除,但实际数据块还在文件里,只是索引被清掉了。这时你可以用第三方工具比如Percona Data Recovery Tool for InnoDB,它能直接扫描ibdata1文件,把那些被标记删除但还没被覆盖的数据行提取出来。这个过程非常慢,一个几GB的文件可能要跑几个小时,而且提取出来的数据是乱序的,你还得自己写脚本排序和去重。更麻烦的是,如果表结构已经没了,你还需要知道原先的字段定义才能把二进制数据解析成可读内容。
说到这,我得泼盆冷水。上面这些方法都有很大的局限性,成功率完全看人品。binlog恢复的前提是你有完整的日志链,中间不能有purge操作。物理文件恢复要求你立刻停止服务且磁盘没有被覆盖。ibdata1扫描要求你熟悉InnoDB的数据页结构。真正靠谱的做法只有一种:定期备份。别再跟我说“我项目小不需要备份”这种话,我见过一个创业公司因为删库损失了三个月用户数据,直接导致融资失败。备份方案可以很简单,每天凌晨用mysqldump全量导出一次,加上binlog实时归档,成本几乎为零。你只需要写一个cron脚本,再加个定时任务上传到对象存储,连服务器磁盘都不用占。
很多人备份了但恢复的时候才发现备份文件是坏的,这才是最绝望的。测试备份的可用性比备份本身更重要。我建议每个月至少做一次恢复演练,在你的测试环境里模拟删库场景,然后拿备份文件去恢复。你会发现很多问题:比如备份文件太大,导入时内存不够;或者备份时没加--single-transaction导致锁表,影响线上业务;又或者备份文件里包含的系统变量和生产环境不兼容。这些问题不演练是永远发现不了的。我认识一个DBA,他每个季度都会搞一次“故障演习”,故意删除一个数据库,让团队成员在规定时间内恢复,恢复失败的去买下午茶。这种文化一旦建立起来,整个团队对数据安全都会变得特别敏感。
说点实在的。如果你现在正看着空荡荡的数据库发愁,先别慌,立刻停止所有写操作,包括应用服务和定时任务。然后检查mysqld进程是否还在运行,在的话先别杀,用lsof命令看看被删除的文件句柄是否还能访问。接着找一下有没有最近的mysqldump备份文件,哪怕是一周前的也行,总比没有强。如果以上都不行,那就认栽,但一定要从这次事故里学到教训。我见过太多人删库之后第一反应是上知乎发帖求助,而不是立刻去检查备份。记住,数据恢复这件事,90%靠预案,10%靠技术。没有预案的时候,再牛的技术也只能看运气。下次再有人问你MySQL删库了怎么办,你就告诉他:先看看备份在哪,如果没有备份,那这篇文章就当是给他上的一课。


