KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA

03 · 迁移与结构版本 — keel 龙骨

类型:主题概念 · 应用课对应:06 章 · 升级与回滚:要么升上去,要么原样退回来

类型:主题概念 · 应用课对应:06 章 · 升级与回滚:要么升上去,要么原样退回来

这一页只回答四件事:它为什么会出现、它是什么、它长什么样、它怎么工作。它不讲怎么设计表结构。

一、它为什么会出现

一次回滚:命令跑完,退出码是 0,屏幕上没有多说什么。看起来退回去了。

然后起栈,worker 的探针当场喊出真相:

[FAIL] 库结构版本:3,镜像要求 2 —— 先跑 `board migrate`

紧跟着一句 container board2-worker-1 is unhealthy。那个退出码为 0 的命令,其实什么都没做。

原因是执行它的那个镜像不认识第 3 版结构——在它看来,库本来就在它认识的那一版上,所以目标版本与当前版本一比,它认为"已经在位"。

需求一句话:库结构会随版本往前变,所以它必须被版本化、能被读出来,也必须能退回去。

二、它是什么

一句话:迁移是"升级 = 跑到目标版本 / 回滚 = 退到目标版本"的同一段代码;结构版本,就是 schema_migrations 里那个最大的版本号。

它不是下面这三样东西:

三、它长什么样

它首先是一张表,加一组三元组。库这一侧的读数只有一行:

SELECT MAX(version) FROM schema_migrations;

那个值就是现场自检里报出来的「结构版本」:

结构版本                       2

代码这一侧,每一版都是一对上、下行语句:

MIGRATIONS = (
    (1, "CREATE TABLE IF NOT EXISTS tasks ...", "DROP TABLE IF EXISTS tasks;"),
    (2, "ALTER TABLE tasks ADD COLUMN priority ...", "ALTER TABLE tasks DROP COLUMN priority;"),
    (3, "ALTER TABLE tasks ADD COLUMN claimed_at ...", "ALTER TABLE tasks DROP COLUMN claimed_at;"),
)

跑一次"往下走",日志会说清它做了什么:

{"event": "migrate.step", "direction": "down", "version": 3}
{"event": "migrate.done", "from": 3, "to": 2}

direction 有两种取值(up / down);而 from 与 to 之间差了几版,才是这次动作真正覆盖的范围。

四、它怎么工作

从"我要把库挪到第 N 版"到"结构真的停在第 N 版",四步:

① 读现状   查 schema_migrations 的最大版本号,这就是"现在在哪一版"
   ↓
② 定方向   目标比现在大就往上升、小就往下降;步集合按方向排序(往下走要倒着来)
   ↓
③ 逐条执行 一版一版地跑:上行就记一条版本,下行就删一条版本,每版各自提交
   ↓
④ 报终点   打印 migrate.done 的 from / to —— 这两个数才是"它到底做了什么"

第 ③ 步是理解一切的关键:每一版各自提交,所以"迁移失败"很少是"什么都没发生",而是"停在中间"。 这也解释了为什么升级失败之后不能只是"切回旧镜像重来"——结构不会自己退回去。

五、它会带出哪些概念

六、回到应用课

应用课里的那句话 现在你知道它为什么成立
"迁移不是一段 SQL,而是一组 (版本号, 上行, 下行) 三元组" 因为回滚走的就是同一组语句的另一个方向,缺了下行就没有回滚
"回滚时执行迁移的镜像,必须是认识新结构的那一版" 不认识第 3 版的镜像看库本来就"已经在位",于是 --to 变成一次静默的空操作
"一个退出码为 0 的动作,不等于它做了你以为的那件事" 命令成功只说明它没报错;它做没做,要看 migrate.done 的 from 与 to
"结构不会自己退回去" 结构是被一版一版提交出来的,往回走也必须一版一版地执行下行语句

这一页改变了你的哪个判断:看到"迁移成功"时,你不再只看退出码,而是先问**"它从哪一版走到了哪一版"**——from 等于 to,就是"它根本没动"。

→ 这一页的展开在应用课 06 章 · 升级与回滚:要么升上去,要么原样退回来。

自测题

  1. 为什么"迁移失败"通常意味着"停在中间",而不是"什么都没发生"?
  2. 一个命令退出码是 0,你凭什么判断它到底有没有改结构?

进入 keel 阅读