资讯文章 资讯 · NEWS
改表结构的脚本也该进版本库:目录站的数据库迁移该怎么管
2026-10-24 约 1,349 字 预计阅读 3 分钟 yiyunkj
改
目录站上线三年,表结构改动少说也有几十次。如果这些改动散落在运维的聊天记录里,开发环境能跑、生产报错就是迟早的事。
迁移工具解决的不是"怎么改表",而是"谁改过、改了什么、现在是什么状态"这三个问题。工具本身很简单,难的是纪律。

图片来源:网络,版权归原作者所有,侵权请联系删除
命名一旦定下就别改
Flyway 的文件名是 V{版本}__{描述}.sql,中间是两个下划线,写一个会直接解析失败。已执行过的版本化脚本按版本顺序跑一次,绝不重复。
| 前缀 | 用途 | 行为 |
|---|---|---|
| V | 版本化迁移 | 按版本顺序执行一次,内容变了会报校验和不一致 |
| R | 可重复迁移 | 校验和变了就重跑,适合视图、存储过程 |
| U | 撤销迁移 | 与某个 V 对应,Flyway 社区版不带这个功能 |
Liquibase 不用文件名排序,靠 changeset 的 id 加 author 唯一标识,格式可以是 XML、YAML 或 SQL,一个 master changelog 串起来。回滚是它自带的,这也是它和 Flyway 最实际的分界线。
校验和报错不是麻烦,是保护
改了一个已经跑过的脚本,Flyway 启动时会直接拒绝:校验和对不上。很多人第一反应是关掉 validate-on-migrate,这是把报警器拆了。
正确做法是永远追加新版本。昨天加的字段类型写错了,今天就写 V12 去修正它,不要回去改 V11。这样开发环境从零建库、生产环境从 V10 升级,走的路径虽然不同,最终状态一致。
Liquibase 同理,changeset 一旦执行就当作不可变。真出了需要改的情况,用 rollback 回到上一个 tag 再重来。
回滚脚本的三种写法
DDL 的麻烦在于它经常不可逆。DROP COLUMN 能写反向语句,但列里的数据已经没了。ALTER TABLE ... MODIFY 缩短长度,回滚时超长的行放不回去。所以回滚要分三类处理:
- 能直接反向的:加列、加索引、建表。反向语句写在回滚脚本里,Liquibase 还能自动生成一部分。
- 需要补数据的:删列之前先把值存进一张备份表,回滚脚本从备份表写回去,并约定备份表什么时候清。
- 不该有回滚脚本的:涉及数据清洗、加密、去重的迁移。这类改动错了就错了,回滚只会更乱,正确做法是先在从库或小流量上验证,再一次性推进。
迁移和代码发布分开跑
这是最容易踩的一条。代码和迁移放在同一次发布里,等于假设两边一定同时成功。更稳的顺序是业内常说的展开与收缩三段:
第一段,加新结构但老代码不用它(加列、加索引、允许为空);第二段,发新代码开始读写新结构,同时兼容旧结构;第三段,确认全量运行一段时间之后,删掉旧结构和兼容代码。
三段之间隔多久取决于你的回滚窗口。把第一段和第三段写进同一个脚本,等于放弃了中间出问题的退路。
CI 里跑三种库状态
只在本机跑一次迁移说明不了什么。CI 里至少覆盖三种起始状态:空库一路迁到最新、从上一版本升到最新、以及从最新状态回退。前两种保证新环境和老环境都能到同一个终点,第三种验证回滚脚本不是摆设。
再补两条配置纪律:生产环境开 clean-disabled,clean 会清空整个 schema,误操作一次就是灾难;ORM 的自动建表在生产上设成 none 或 validate,让迁移工具独管结构,两套东西同时改表必然打架。
给目录站加字段这件事,真正贵的不是写那条 SQL,是三年后没人说得清这张表为什么长这样。