没有台账时的三种混乱
项目前期迁移脚本是纯手动的:sql/migrations/ 目录下按日期命名,人工按顺序执行。脚本攒到 40 多个时,问题集中出现:
- 不知道跑了哪些:客户现场报错,第一句是"你执行到哪一条了",没人答得上来;
- 重复执行:两个同事先后跑同一批脚本,第二条就报"表已存在",之后靠人肉跳过;
- 改了历史脚本:有人为了修一个小问题,直接改了上周的脚本,导致新环境和老环境结构不一致。
这三个问题的共同点是:缺少"执行过什么"的事实记录。
台账表:每一行是一条历史事实
迁移台账表只做一件事:记录每个脚本什么时候执行、执行结果如何、内容有没有被改过。
| 字段 | 作用 |
|---|---|
| 版本/文件名 | 唯一标识一条迁移 |
| 校验和(SHA-256) | 记录执行时文件内容的指纹 |
| 批次 | 同一次执行的所有脚本属于同一批,便于回滚定位 |
| 耗时 | 排查慢迁移 |
| 状态 / 错误摘要 | 成功或失败,失败保留原因 |
校验和是台账最重要的设计:命令在每次执行前比对"文件当前指纹"与"台账里记录的指纹",不一致就直接拒绝继续。
这条规则拦住的是最常见的一类事故:有人修改了一个已经在客户环境执行过的脚本。结构会悄悄分叉,而且不报错。
命令的三个动作
```
--dry-run 只列出待执行清单,不改库
--status 查看已应用/待执行/校验和不一致
--baseline 存量库首次接入:把已有脚本标记为"已应用"而不真正执行
```
--baseline 解决的是一个很实际的场景:客户库里已经有完整结构(可能是早期人工建的),此时如果直接跑全部迁移,第一条建表就会失败。baseline 的作用是"承认现状,从今天开始记账"。
规范里写得很明确:存量库首次接入必须先 baseline,否则会从第一条重跑。
单个脚本失败就停
执行策略是"顺序执行,遇错即停":
- 当前脚本在事务里执行,失败则回滚并写状态与错误摘要;
- 后续脚本不再执行;
- 修复后重跑,会从这条失败脚本继续(因为它的状态不是"已应用")。
比"跳过失败继续跑"更好的原因很简单:迁移之间通常有依赖(先建表再改列),跳过会造成更隐蔽的结构错误。
文件切分规则要与建库脚本一致
迁移文件里可以有多条语句,切分规则必须与建库导入器完全一致(跳过注释与空行、按行尾分号提交)。
我们在这里错过一次:导入器按"行尾分号"切分,迁移命令按"整行只有分号"切分,结果某些写成一行多语句的脚本,在两条路径下行为不同。统一规则后,这类问题消失。
一条纪律
已应用的迁移文件不得再修改。 需要修正就新增一条迁移。
这条纪律听起来苛刻,但它是"客户环境可复现"的唯一保障。一旦允许改历史脚本,任何两个环境都可能不一样,而你不知道差在哪。