Immich 数据库迁移实战指南:从改 schema 到回滚的完整链路
【免费下载链接】immichHigh performance self-hosted photo and video management solution.项目地址: https://gitcode.com/GitHub_Trending/im/immich
在 Immich 项目里,服务端的表结构是用 TypeScript 写出来的,但改这些文件并不等于 PostgreSQL 的结构会跟着变,中间要靠一次数据库迁移来落地。这套机制管理着三件事:迁移文件的生成、执行顺序的登记(ORDER 清单)、以及应用后的回滚与漂移检测。面向刚接手 server 代码的开发者,读完这篇可以独立走完一次完整的 schema 变更,并知道每个环节该自查什么。
机制速览:它到底在管什么
整个体系分两条线:
- 声明线:server/src/schema/tables/ 下 64 个表定义文件,加上
enums.ts、functions.ts,描述"数据库应该长什么样"。改它们只改了图纸,没碰真实库; - 执行线:
server/src/schema/migrations/下 96 个带毫秒时间戳前缀的迁移文件,每个文件含up()与down()两个函数,才是真正对数据库执行变更的指令。
两条线之间靠@immich/sql-tools(版本 0.6.3)桥接:它比对声明式 schema 与真实数据库的差异,自动生成迁移 SQL。新手容易困惑的另一个文件是 migrations/ORDER,每行对应一个迁移名,是执行顺序的权威登记簿。
- 迁移文件名 = 时间戳 + PascalCase 名称,目录内按字典序即执行序;
- 首条为
1744910873969-InitialMigration,最新一条是1787148183730-DeleteMismatchedMemoryAssets; - 个别迁移的
up/down是空操作,只为维持 ORDER 清单与磁盘文件的对应关系。
从改代码到落库的完整路径
用 generate 命令生成迁移文件
先比对声明式 schema 与本地库差异、产出迁移文件,跑这条:
mise //server:migrations generate <migration-name>//server:表示在 monorepo 根目录执行 server 包的任务,定义见 server/mise.toml,展开后就是sql-tools -u <DB_URL> migrations generate。未设置DB_URL时默认连postgres://postgres:postgres@localhost:5432/immich,即本地开发环境的 Postgres。产出的文件名形如1745244781846-AddUserAvatarColorColumn.ts,但此时还不在最终目录。
人工过目 up/down 内容
移动文件之前先打开检查三件事:up生成的 DDL 是否符合预期、存量数据回填是否齐全、down能否安全撤销。以仓库里的真实迁移为例:
export async function up(db: Kysely<any>): Promise<void> { await sql`ALTER TABLE "users" ADD "avatarColor" character varying;`.execute(db); // 回填:从 user_metadata 的 JSON 里取出 avatar 颜色写入新列 await sql` UPDATE "users" SET "avatarColor" = "user_metadata"."value"->'avatar'->>'color' FROM "user_metadata" WHERE "users"."id" = "user_metadata"."userId" AND "user_metadata"."key" = 'preferences';`.execute(db); } export async function down(db: Kysely<any>): Promise<void> { await sql`ALTER TABLE "users" DROP COLUMN "avatarColor";`.execute(db); }完整见 1745244781846-AddUserAvatarColorColumn.ts。up先加列再回填,down只删列——注意回填之后新写入的数据在回滚时是拿不回来的。
把文件放进 migrations 目录
将生成的文件移入 server/src/schema/migrations/。文件名带时间戳前缀,同目录内字典序天然就是执行顺序,这一步只是把它放到对的位置。
用 sync-order 命令登记执行顺序
生成文件不会自动进入 ORDER 清单,补跑这条:
mise //server:migrations sync-order它把新迁移名追加进ORDER。这份清单被 git 跟踪是有意的:两个分支各加一条迁移时,会在这个文件上产生冲突,逼你显式决定谁先谁后。如果只依赖文件时间戳,两边会"静默合并"且顺序可能颠倒——后执行的 DDL 可能依赖还不存在的表,服务直接起不来。说白了,是故意制造一点合并冲突,换顺序的确定性。
启动时发生了什么
开发模式下*.ts文件变更会触发 server 自动重载,而"执行所有未应用的迁移"就内嵌在启动流程里。所以只要本地 server 重载,新迁移即刻打进本地库,不用手动run。
CI 侧有对应把关:server/mise.toml 里的checklist任务在单测与中测之后固定追加一项校验:
{ task = ":migrations", args = ["verify-order"] }verify-order核对磁盘迁移文件与ORDER清单完全一致,防止有人漏掉登记这一步。不想走 mise 的话,server/package.json 里有一组等价脚本,在server目录下直接跑:
| 脚本 | 作用 |
|---|---|
migrations:generate | 比对 schema,生成迁移 DDL |
migrations:run | 执行所有未应用的迁移 |
migrations:revert | 回滚最新一条迁移 |
migrations:sync-order/migrations:verify-order | 登记 / 校验ORDER清单 |
想撤销时怎么回滚
🔄 需要撤销最近一次已应用的迁移时:
mise //server:migrations revert它会执行最新迁移的down(),把数据库恢复回这次迁移执行前的状态。适用边界:仅限本地开发或测试场景(比如验证自己写的down是否真可逆);down通常对数据变更不可逆,生产环境严禁执行这类操作。
校验与排错
漂移检测:仓库内置schema-check服务命令(实现见 server/src/commands/schema-check.ts),把每条迁移归为applied、deleted(库里已应用但文件丢失)、missing(文件存在但未执行)三类,再比对真实库与声明式 schema 找出漂移项;检出漂移时打印自动生成的修复 SQL,源码里明确标注"Use at your own risk"——那段 SQL 仅供定位问题,执行前必须人工确认。
本地一键重建:本地库被手改、与迁移历史脱节时,一条命令重建:
mise //server:schema-reset先DROP SCHEMA public CASCADE清空后,再按ORDER顺序重放全部 96 条迁移,得到与代码完全一致的干净库。注意:该操作清空所有数据,只准用于本地开发库。
CI 校验:mise //server:migrations verify-order是提交前的最后关卡,CI 的checklist会替你跑。
动手前自检清单
- 本地 Postgres 能连通(默认
DB_URL指向localhost:5432/immich); - 生成后逐项确认
up/down与数据回填逻辑,不直接信任自动 DDL; - 迁移文件已移入
migrations/目录,不是留在默认产出位置; ORDER清单随迁移文件一起提交,没漏跑sync-order;- 提交后
verify-order通过,schema-check无漂移。
前提是本地有可连接的 Postgres,且使用本仓库@immich/sql-tools 0.6.3工具链;schema-drop/schema-reset仅限本地开发库,生产环境请勿照搬。
【免费下载链接】immichHigh performance self-hosted photo and video management solution.项目地址: https://gitcode.com/GitHub_Trending/im/immich
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考