迁移的心智模型:历史、影子库与漂移
本教程共 54 篇 · 第 29 篇 · 更新于 2026-08-11 · 约 6 分钟阅读
本节目标:理解迁移的本质——数据库 Schema(模式)的版本控制;认识迁移历史、迁移表、影子数据库三件套,以及为什么已应用的迁移碰不得。
数据库也需要版本控制
代码用 Git 管版本,数据库的结构却常常没人管。上线后有人手动加了列,开发环境改过表结构,几个月后没人说得清生产库长什么样。迁移(migration)就是给数据库结构做的版本控制:每次结构变更记录成一份 SQL 文件,按顺序应用,随时可以重放。
演进 Schema 有两条路线:
- 模型优先:先用代码定义结构,再由迁移工具生成 SQL 应用到数据库。Prisma Migrate 走这条路。
- 数据库优先:先用 SQL 建库,再通过内省(Introspection)反向生成代码。
db pull属于这条线。
本部分讨论的都是模型优先。
四份状态
Prisma Migrate 靠四份状态跟踪数据库:
- Prisma Schema:唯一源头,描述目标结构
- 迁移历史:
prisma/migrations/下的 SQL 文件 - 迁移表:数据库里的
_prisma_migrations表,记录已应用的迁移 - 数据库本身:实际的表结构
Schema 是「应该长什么样」,迁移表是「已经做了什么」,数据库是「现在长什么样」。三者一致,一切正常;不一致,就出问题。
开发环境的三个前提
Prisma Migrate 对开发环境有几个要求:
- 每个环境一个数据库:开发、预览、生产各用各的库
- 开发库可以随意丢弃:随时能建、能删、能重置
- 各环境数据库配置一致:同一份迁移在不同环境产生相同结果
前两条保证了「重置数据库」是低成本操作,第三条保证了迁移历史在各地重放结果一致。如果你的数据库托管在云端、不允许自由建删库,开发环境就要单独准备一个影子数据库(下文会讲)。
migrations/ 目录与 migration_lock.toml
每次 migrate dev 都会生成一个以时间戳命名的文件夹:
prisma/migrations/
├── migration_lock.toml
└── 20260811120000_init/
└── migration.sql
时间戳就是应用顺序——迁移按字典序执行。整个目录必须提交进版本库:migrate deploy 只认迁移文件,不读 Schema;而且手改过的迁移里藏着 Schema 表达不了的信息(比如数据回填 SQL),丢了就再也找不回。
migration_lock.toml 记录数据库厂商(provider)。它防止你中途换库:迁移文件是厂商相关的 SQL,PostgreSQL 的语法不能用在 SQLite 上。强行切换会报错 P3019(provider 与 migration_lock.toml 不匹配,部分旧文档写作 P3014),提示清空迁移历史重新开始。
_prisma_migrations 表:带 checksum 的账本
迁移应用到数据库后,_prisma_migrations 表会记一笔账:迁移名、应用时间、错误日志,还有一个 checksum(校验和)。
checksum 是迁移文件内容的哈希。再次运行迁移命令时,Prisma 会重新计算文件哈希,和账本里的比对。一旦对不上,就说明有人改过已应用的迁移——这被视为「迁移历史冲突」。
Note分工不同:影子数据库负责检测结构漂移,checksum 负责检测文件被篡改。
影子数据库:漂移检测器
影子数据库(shadow database)是 migrate dev 运行时临时创建、用完即删的数据库,主要干四件事:
- 把现有迁移历史在影子数据库里重放一遍
- 内省影子数据库,得到「历史终点」的结构
- 与你的开发数据库对比,发现意外改动——这就是漂移(schema drift)检测
- 生成新迁移,并评估是否会导致数据丢失
开发库被手动改过(比如手敲了 SQL),对比就会报漂移,migrate dev 会提示重置数据库,并具体列出差异。例如枚举类型缺了 RED 变体、多了 TRANSPARENT 变体,输出会逐项标出 [+] 新增和 [-] 移除。
Note影子数据库只在开发环境需要。
migrate deploy、migrate resolve这些生产命令完全不碰它。
本地 PostgreSQL 用户需要 CREATEDB 权限。云数据库(如 Heroku、Vercel Postgres)不允许自动建库,要在 prisma.config.ts 里手动指定:
datasource: {
url: env("DATABASE_URL"),
shadowDatabaseUrl: env("SHADOW_DATABASE_URL"),
},
Warning
shadowDatabaseUrl绝不能和url指向同一个库,否则迁移过程会清空你的数据。
db push:不记历史的同步
快速原型阶段可以先用 prisma db push:它直接把 Schema 同步到数据库,不生成迁移文件、不记录历史。适合丢弃型实验。但它无法预览变更、无法编排数据迁移,检测到破坏性变更只会提示重置——所以正式项目从第一天就该用迁移。
已应用的迁移不可编辑
迁移一旦应用,就是历史。修改已应用的迁移文件(哪怕只是把 VARCHAR(550) 改成 VARCHAR(560)),会导致开发与生产的历史分叉:开发环境重置后能重放,生产环境却永远带着旧文件,migrate deploy 从此每次都会警告「以下迁移已被修改」。
这种「看似无害」的改动最危险:重置后重放一切正常,你会以为没事,但生产环境的警告会一直存在,哪天改动涉及高度定制的迁移,开发与生产的行为就彻底对不上了。
正确的姿势:用新迁移修正旧迁移。想要 560,就新建一个迁移把列改过去。
Tip发现已应用的迁移被改或缺失时,优先恢复文件、还原改动,而不是重置数据库。治本,不治标。
Note本部分所有命令仅适用于关系型数据库。MongoDB 在 v7 中不被支持,需要 MongoDB 请留在 v6.19。
参考来源
- Prisma 官方文档:Mental model
- Prisma 官方文档:Migration histories
- Prisma 官方文档:Shadow database
- Mapagam:Creating and running migrations