site logo

Marico's space

20年SQL迁移工具的6条经验教训(应用于2026年的NoSQL迁移器)

算法解析 2026-09-24 11:27:53 5

最近折腾了一把自己的 NoSQL 到 SQL 数据迁移工具,翻了不少资料,发现一个挺有意思的现象:SQL 迁移这个领域积累了快 20 年的工程经验,Flyway、Liquibase、dbmate 这些工具把schema 变更这个难题玩出了花,但隔壁 NoSQL 迁移工具圈子里,这些经验几乎没人用。不是说这些知识保密——全是开源的,文档齐全,大厂生产环境验证过的——就是没人把它们搬过去。

所以我先去把老工具的原理研究了一遍,这篇把我觉得值得借鉴的 6 条经验说清楚,也给正在做类似事情的同学一点参考。

1. 约定优于配置

Flyway 通过文件名来决定执行顺序:V2__add_orders_table.sql。不需要配置文件,不需要记录哪个脚本跑过哪个没跑过,文件名里的数字本身就是执行顺序。

应用到 NoSQL 迁移:每个迁移步骤应该生成一个带版本号的、命名可预期的文件,放在你自己的文件系统里,可以在 git 里 review——而不是只存在于工具内存里的隐藏状态。

2. 把"脏状态"当成安全机制,而不是麻烦

如果 Flyway 迁移执行到一半失败了,它会把 schema 标记为"脏"状态,然后拒绝运行任何后续脚本,直到有人工介入确认安全后才会继续。

有意思的地方不在于状态追踪,而在于"拒绝"本身。我见过的很多工具,遇到错误时的策略是"继续重试"。Flyway 的做法相反:停下来,让人类决定。在你可能悄悄破坏别人生产数据的场景下,这是一个更好的默认选择。

3. 每个步骤执行完后都输出 schema 快照

dbmate 每次迁移后会写一个可读的 db/schema.sql——完整的当前状态,随时可以在 git 里做 diff。

这听起来简单到不像个"经验",但它改变的是信任的形态。你拿到的不是一条需要解析的日志,而是一个可以直接阅读的文件。每个步骤都应该留下点实在的东西,而不是一条滚动过去就消失的终端状态信息。

4. 保持是一个小巧、无趣的二进制文件

dbmate 刻意保持极简:一个二进制文件,纯 SQL,连接字符串用一个环境变量搞定。没有 ORM,没有插件系统,没有框架相关的约定渗透到你的 schema 里。

在这里,无趣是一种优势。当一个迁移工具需要自己的插件生态才能发挥作用时,它已经不再是迁移工具了,而是变成了一个平台——一个更难长期免费维护的东西。

5. 回滚是用户的责任,工具应该明确说出来

在 Liquibase Community 里,你要自己写回滚脚本。工具不会发明一个神奇的"撤销"按钮。

这比听起来更诚实。一个通用的"撤销"操作往往并不真的安全——取决于中间发生了什么,干净地回滚一个迁移可能和执行它是一个完全不同(也更难)的问题。假装不是这样,就是让人去信任一个悄悄把事情弄得更糟的回滚操作。

6. 把"改了什么"和"怎么跑"分开

Liquibase 把 changelog(SQL、YAML、JSON、XML,随你选)作为独立于执行引擎的产物。你可以读取、review、版本化管理 changelog,而不需要实际运行任何东西。

对于任何涉及推断结构的场景——这恰恰就是 NoSQL 到 SQL 迁移需要做的事情——这个分离更加重要。提议的 schema 应该是人类可以在一个文件里冷静地阅读和修改的东西,在任何东西碰到真实数据库之前。而不是在 CLI 提示符里一闪而过、本能地点个"确认"就过去的东西。

为什么这些经验之前没人应用到 NoSQL 迁移

两个原因,都比较实在。

第一:关系型数据库 schema 迁移有一个稳定的"形态"——SQL 是标准,所以针对它构建的工具可以泛化到任何使用 PostgreSQL 或 MySQL 的项目。NoSQL 到 SQL 迁移没有这个稳定的形态。Firestore、MongoDB、DynamoDB 各自建模数据的方式差异大到离谱,围绕其中一个构建的工具没法平滑迁移到另一个,这使得打磨单个工具的投入产出比很小。

第二点更有意思:即便是 DBeaver 这样有 800 万+用户、有真实融资的数据库客户端,NoSQL 支持也是付费功能,而不是免费附带的。这释放了一个信号:经过 20 年开源维护打磨的关系型工具市场已经足够大且稳定,而 NoSQL 迁移工具市场到目前为止还没有到这个规模。

这些都不是跳过这些经验的理由。恰恰相反——这解释了为什么之前没人这么做过。

我正在用上面 6 条模式构建一个 Firestore 到 PostgreSQL 的迁移工具(Centauri Migrate)——带版本号的步骤、有脏状态保护、推断后输出可读的 schema 快照、默认 dry-run、还有一个人工 review 过的 changelog。还在早期阶段,84 个测试用例,还没拿真实生产数据跑过——但架构层面不是靠猜的,是从已经验证过可行的工具里借鉴来的。

凌晨三点肝完的,手里还端着早上那杯咖啡 ☕