前两天半夜两点,我被一个电话吵醒。电话那头是个 DBA,声音都变了调:“哥,我们生产库的 pg 库被人 rm 删了,物理文件全没了。”我叹了口气,问他备份情况如何。他沉默了三秒,说:“备份在同一台机器上。”得,这下连备份一起没了。这种事在 DBA 圈子里不算新鲜,但每次遇到,都像被人往心口捅了一刀。PostgreSQL 作为现在最火的开源数据库之一,用的人越来越多,可很多人只关心怎么建表、怎么写 SQL,对备份恢复这件事,基本是“等出了事再说”的心态。

先说最绝望的情况:物理文件被删了,而且没有外部备份。这时候能不能恢复?答案是看运气。Linux 系统里,文件被 rm 删除后,如果所在磁盘分区没有被大量写入新数据,文件内容其实还躺在磁盘上,只是 inode 被标记为可覆盖。这时第一件事就是赶紧把数据库停掉,然后卸载分区,用 dd 或者 extundelete 这类工具扫描磁盘,把残留的数据块捞出来。但这里有个坑,PostgreSQL 的数据文件是分段的,尤其是 WAL 日志和表空间的布局很复杂,就算把数据块捞出来,也很难拼成一个可用的数据库。我见过有人成功恢复过,但那是因为他删文件后立刻停机,而且磁盘空间大、碎片少,恰好运气爆棚。多数情况下,能恢复出一半的数据就算是奇迹了。
比物理删除稍微温柔一点的是误执行了 DROP TABLE 或者 TRUNCATE。这类操作走的是 PostgreSQL 内部的事务机制,理论上只要没做 VACUUM FULL,并且 WAL 日志还在,就有机会通过时间点恢复(PITR)捞回来。前提是你得有归档日志,还得有基础备份。很多团队觉得“我们有 pgdump 就行了”,但 pgdump 只能恢复到备份那一刻的数据,如果备份是昨晚 12 点做的,你今早 10 点误删了表,那中间 10 个小时的数据全丢。而 PITR 配合连续归档,可以把数据库恢复到任意一个时间点,包括误操作之前的那个瞬间。这个功能 PostgreSQL 从 8.0 版本就有了,但据我观察,真正配置了完整归档和 WAL 保留策略的生产环境,连三分之一都不到。
再说一种更隐蔽的误删除:有人以为自己在某个模式下删表,结果把整个 schema 甚至整个库给 DROP 了。这种情况在开发环境和测试环境里尤其常见,因为权限控制往往很松散。我见过一个案例,开发人员在 Navicat 里点了“删除数据库”,结果把同一台服务器上另一个项目组的库也删了——原因是连接串写错了。这类事故的恢复难度取决于是否有独立的外部备份,以及备份策略是否覆盖了所有库。很多公司为了省事,只备份了核心业务库,边缘项目的库根本不备份。等到出事才发现,那个“不重要”的库里的数据,其实关联着好几个关键报表。
现在聊聊工具。市面上能用来恢复 PostgreSQL 数据的工具不少,比如 pgrecovery、pgdirtyread、pgfiledump,还有 PostgreSQL 自带的 pgresetwal 和 pgrewind。但这些工具各有使用场景和限制。pgdirtyread 可以在表仍物理存在但数据被标记删除时读取死元组,前提是表没有被 VACUUM 清理过。pgfiledump 能解析数据文件,但需要你懂 PostgreSQL 的页面结构,不然一堆二进制数据看得眼花。最麻烦的是,很多恢复操作需要手动修改系统表,如 pgclass、pg_attribute,改错了整个库可能直接崩溃。这不是小白能干的事,也不是普通 DBA 能随便搞定的活。
说到这儿,得提一个血淋淋的教训:千万别在生产环境用 rm -rf 删东西,哪怕你觉得自己“确定在删对的文件”。有个哥们儿在凌晨三点做数据清理,一个脚本跑下去,直接把整个数据目录干掉了。他当时还觉得奇怪,怎么删得这么快?后来发现脚本里的路径写错了。更讽刺的是,他前一天刚写完一篇博客,标题叫《PostgreSQL 生产环境安全操作指南》。所以,权限管理、操作审计、双人复核这些流程看着繁琐,但关键时刻能救你一条命。很多公司把 DBA 当成“会写 SQL 的工具人”,从不给他们配操作审批流程,出了事又怪人,这是管理上的失职。
说点实在的。如果你现在正在用 PostgreSQL,赶紧做三件事:第一,检查备份策略,确保有至少一份备份存放在不同机器或云存储上,别和数据文件放在同一块盘。第二,配置连续归档和 WAL 保留,设置合理的恢复目标时间(RPO)。第三,写一份详细的灾难恢复手册,并且每年至少演练一次。别觉得这些事麻烦,等你真遇到数据丢失的时候,你会发现之前花在备份上的每一分钟,都是值得的。我见过太多人在出事后痛哭流涕,说“早知道就做备份了”。但数据恢复这件事,从来没有“早知道”,只有“来不及”。


