真正让我崩溃的不是恢复失败,而是一次"成功"的恢复——它悄无声息地把数据库弄错了状态。三个月前的Postgres备份导入到测试环境,应用跑起来一切正常,直到同事发现少了两列数据。迁移脚本在第六周加过这两列,但备份文件里根本没有迁移记录。两个东西完全分离,由不同的流程管理,没人保证它们会一起出现。

如果你做过两三个项目,一定见过那个文件夹。通常叫scripts/、db/,或者直接扔在仓库根目录。里面塞着各种备份脚本、迁移记录、手动修复的SQL片段。

打开网易新闻 查看精彩图片

核心问题不是懒,而是备份和迁移被当成两件事,实际上它们紧密耦合。没有迁移状态的备份是不完整的,你不知道快照拍下来时哪些迁移已经跑过。三个月后恢复,你只能猜——要么全跑一遍,要么一个都不跑,两种选择都可能是错的。我见过这两种错误都在测试环境搞丢过数据,有一次甚至上了生产环境。

我真正需要的是把迁移状态"烤"进备份本身,恢复时自动知道该跑哪些迁移、跳过哪些。不是手动维护的约定,不是承诺会更新的README,而是工具层面的硬性保证。Portabase v1.13做的就是这件事,也是我切换的原因。注意:本文针对v1.13,如果你还在用v1.12,--migration-source参数改过名,快照格式也变了,直接升级不读更新日志会搞坏现有恢复脚本。

Portabase最让我意外的是它不做什么。它不想当另一个迁移工具,不和Flyway或Liquibase竞争。它只是读取这些工具已经写进数据库的迁移状态,和SQL dump一起打包成单个归档。这个区别很实用——可惜README把它埋在三个段落的废话后面。

本质上Portabase就是一个Go写的单二进制文件,没有运行时包袱。扔到服务器上,指一下数据库连接字符串就能跑。唯一真正的依赖是DB客户端库——PostgreSQL要libpq,MariaDB要对应的连接器。不用折腾Node运行时,不用管JVM版本冲突,不用配Python虚拟环境。我把12MB的二进制文件塞进Docker镜像的最终阶段,几分钟就搞定了。