这事儿得从一件真实的案例说起。前阵子我一个朋友,在公司做电商,运营主管半夜手滑,在数据库管理工具里执行了一条 DROP TABLE 命令,直接把用户订单表删了。当时他吓得一身冷汗,赶紧给我打电话,声音都在发抖。我问他是删了数据还是删了表结构,他说表没了,连数据带结构一起“一锅端”。我让他先别慌,别往生产库写任何数据,然后远程上去一看,还好二进制日志开着,定期备份也有。折腾了两个小时,总算把数据从昨天的全量备份里捞出来,再通过 binlog 把增量部分追回来,损失控制在半小时内。这事儿要是发生在没开 binlog、没做备份的环境里,那真是哭都没地方哭去。所以聊数据库恢复,第一件事不是技术,而是观念——你得知道,删库不是世界末日,但没有准备就是。

恢复的核心其实只有两条路:一是靠备份还原,二是靠日志回放。备份还原最直接,前提是你得有备份。常见的备份方式有 mysqldump 全量备份、XtraBackup 物理备份,还有主从复制的延迟从库。如果你每天凌晨跑一次 mysqldump,删除后直接拿昨天的备份文件恢复到一个新库,再把今天凌晨到删除时刻之间的增量数据想办法补回来。但问题是,全量备份只能恢复到备份时间点,删除操作发生在备份之后,那从备份时刻到删除时刻之间的数据就没了。这时就得靠二进制日志(binlog)来补。binlog 记录了所有数据变更操作,包括 INSERT、UPDATE、DELETE,甚至 DROP TABLE。只要把 binlog 中从备份时间点到删除时间点之间的操作提取出来,回放到新库,就能把数据追回来。前提是 binlog 必须开启,而且没有被自动清理。
如果不走运,既没有备份又没开 binlog,那情况就棘手了,但并非完全没有希望。MySQL 的数据文件是存放在磁盘上的,删除表只是把 .frm (表结构文件)和 .ibd (数据文件)从文件系统里删掉,操作系统层面的删除其实只是把 inode 标记为可覆盖,物理数据仍然在磁盘上。只要没有被新数据覆盖,就有机会用文件恢复工具找回来。比如在 Linux 下可以使用 extundelete、testdisk 等工具扫描磁盘,找回被删除的 .ibd 文件。找回后把它放回 MySQL 的数据目录,使用 ALTER TABLE … IMPORT TABLESPACE 之类的命令导入。但成功率不高,因为生产环境磁盘读写频繁,往往删完就被新写入的数据覆盖。而且操作复杂,需要了解文件系统原理并拥有 root 权限。所以这只能算作救命稻草,不能作为常规方案依赖。
还有一种情况是误删了部分数据而不是整张表。比如本想执行 DELETE FROM table WHERE id > 1000 ,却忘了加 WHERE 条件,把全表数据删了。这时如果 binlog 是 ROW 格式的,就能用 mysqlbinlog 工具解析出每一条删除操作前的行数据,然后反向生成 INSERT 语句插回去。具体做法是:先定位到对应时间段的 binlog 文件,使用 mysqlbinlog --base64-output=DECODE-ROWS -v 解析出 SQL,找到那些 DELETE 语句并把它们反转成 INSERT。这个过程可以写脚本自动化,或者使用第三方工具如 binlog2sql、my2sql 来辅助。这些工具能直接解析 binlog,生成回滚 SQL,省去手动逐条反转的麻烦。不过要注意,如果 binlog 格式是 STATEMENT 或 MIXED,解析出来的可能不是完整行数据,恢复会更困难。因此强烈建议生产环境使用 ROW 格式的 binlog,虽然占用磁盘稍多,但恢复方便得多。
说到工具,实战中常用的有:Percona XtraBackup 是物理备份的神器,支持在线热备,备份速度快,恢复也快,适合大库;mysqldump 逻辑备份适合小库或只备份部分表;binlog2sql 是阿里开源的,专门用于解析 binlog 生成回滚 SQL,支持精确到时间点或位置的回滚;还有一个叫 MyFlash 的工具,功能类似。这些工具使用起来都不难,但前提是要提前做好环境配置,比如把 binlogformat 设为 ROW, binlogrow_image 设为 FULL,这样才能保证解析出的行数据完整。另外,binlog 的保留时间也很关键,默认可能只有几天,建议根据业务量设置为 7 天或更长,这样即使发现问题较晚,也还能找回数据。很多公司出事就是因为 binlog 只保留了一天,等发现时已经被自动清理,真的没办法了。
另一个容易被忽略的恢复手段是延迟从库。如果你搭建了 MySQL 主从架构,可以把从库的复制延迟设置成 1 小时或 2 小时。这样主库上发生误操作后,从库还没执行该操作,你就能从从库导出数据,再导回主库。这个方案的好处是恢复速度极快,几乎不需要解析日志,直接使用 SELECT … INTO OUTFILE 或 mysqldump 导出即可。缺点是需要额外维护一台从库,而且延迟时间要设得合理,太短可能来不及反应,太长又浪费存储。一般建议设成 1 小时,足以让人手动干预。延迟从库不宜直接对外提供服务,因为数据是滞后的,只能作为灾备恢复专用。很多有经验的 DBA 都会在主从架构里额外配一台延迟从库,专门应付这种手滑操作。
说点掏心窝子的话。数据库恢复技术层面其实就那几招,真正拉开差距的是有没有预案、有没有演练过。我见过太多公司,开发环境随便删表,生产环境虽然开着 binlog,却没人知道怎么用,或者备份脚本跑了好几年却从未测试过恢复流程。等到真出事那天,备份文件损坏、binlog 被清理、权限不足,处处是坑。所以与其研究删库后怎么恢复,不如先花时间做好三件事:一是开启 binlog 并设为 ROW 格式,保留足够长的时间;二是定期做全量备份并测试恢复;三是搭建延迟从库或至少搞一个可回滚的沙箱环境。这三件事扎实了,即使真的删库,也能把损失控制在分钟级别。记住,恢复不是技术问题,而是管理问题。


