Prisma迁移历史与数据库状态不一致问题排查与解决
Prisma迁移历史与数据库状态不一致问题排查与解决
核心问题
Prisma 迁移历史与数据库实际状态不一致,导致 prisma migrate dev 无法正常执行。
问题链路(逐步暴露)
1. 重复迁移文件
现象:20260111065200_add_file_hash 和 20260111065014 两个迁移都添加了 fileHash 列,影子数据库执行时报 column already exists。
原因:开发过程中多次生成了添加同一字段的迁移。
解决:将 fileHash 合并到 init 迁移,删除两个重复迁移文件夹。
2. 修改已执行的迁移导致 checksum 不匹配
现象:The migration was modified after it was applied
原因:修改了 init 迁移的 SQL 内容,但数据库 _prisma_migrations 表记录的 checksum 还是旧的。
解决:用 sha256sum 计算新文件 checksum,手动 UPDATE _prisma_migrations SET checksum = '...'。
3. 删除的迁移仍在数据库记录中
现象:missing from the local migrations directory
原因:本地删除了迁移文件夹,但 _prisma_migrations 表还记录着它们已执行。
解决:清空 _prisma_migrations 表,重新标记。
4. 数据库结构与 schema.prisma 存在 drift
现象:大量索引重命名(_index vs _idx)、关联表约束差异(unique index vs primary key)、外键差异。
原因:之前可能用过 prisma db push 直接同步数据库,db push 生成的数据库结构和 migrate dev 生成的有细微差异(索引命名规则不同)。
解决:
# 1. 用 migrate diff 找出精确差异(从数据库实际状态 → schema.prisma 期望状态,输出的 SQL 就是需要在数据库上执行的修复脚本)npx prisma migrate diff --from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma --script
# 2. 手动执行 SQL 对齐数据库(分三批:约束修复、外键重建、索引重命名)npx prisma db execute --stdin --schema prisma/schema.prisma < fix.sql
# 3. 验证差异为空npx prisma migrate diff --from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma --script# 输出: "This is an empty migration."5. Advisory Lock 超时
现象:Timed out trying to acquire a postgres advisory lock
原因:之前多次执行 Prisma 命令,某些连接未正常释放数据库锁。
解决:重启数据库服务释放所有连接。
最终方案总结
| 步骤 | 操作 |
|---|---|
| 1 | 删除所有旧迁移文件夹 |
| 2 | 手动执行 SQL 对齐数据库结构与 schema.prisma |
| 3 | migrate diff --from-empty --to-schema-datamodel 生成新的 init 迁移 |
| 4 | migrate resolve --applied 标记为已执行 |
| 5 | 如果中途修改过迁移文件,需手动更新 _prisma_migrations 表的 checksum |
经验教训
- **不要混用
db push和migrate dev**:两者生成的数据库结构有细微差异(索引命名等),混用会导致 drift - 不要随意修改已执行的迁移文件:Prisma 用 checksum 校验,修改后必须同步更新
- 不要随意删除迁移文件夹:需同步清理
_prisma_migrations表记录 **migrate diff是排查利器**:可以精确对比 schema、数据库、迁移文件之间的差异
数据库迁移方案
未来如需将数据库迁移到新服务器,可以使用 Navicat 等工具导出导入。
关键点:
prisma migrate dev执行时会做三重校验:
_prisma_migrations表记录 ↔ 本地迁移文件(是否匹配、checksum 是否一致)- 迁移文件执行结果 ↔ 影子数据库(迁移能否在空库上干净重放)
- 迁移文件重放后的预期结构 ↔ 数据库实际结构(是否存在 drift)
本轮对话遇到的问题就是:即使
_prisma_migrations数据完整迁移过来了,但迁移文件有重复、被修改、被删除,或数据库结构与迁移预期有偏差(如之前用过db push),仍然会导致校验失败。所以仅靠导出_prisma_migrations表并不能保证迁移正常。
推荐方案:Prisma 建表 + Navicat 只导数据
这是最稳妥的方式,由 Prisma 完全掌控表结构和迁移记录,避免任何不一致。
# 1. 修改 .env 指向新数据库# DATABASE_URL="postgresql://user:password@new-host:5432/mo_gallery"
# 2. 让 Prisma 在新库创建表结构 + _prisma_migrations 记录(自动执行所有迁移)npx prisma migrate deploy
# 3. 用 Navicat 从旧库只导出业务数据(不含表结构,不含 _prisma_migrations 表),导入新库# 注意:导入时选择「追加数据」而非「覆盖」,避免清空 _prisma_migrations 表
# 4. 验证npx prisma migrate status# 输出: Database schema is up to date!为什么不推荐 Navicat 全量导出(含表结构 + _prisma_migrations):
本轮对话的问题就是前车之鉴。如果旧库本身存在以下问题,全量导出会把隐患一起带到新库:
- 旧库用过
db push,表结构与迁移预期存在 drift(索引命名、约束类型等差异) _prisma_migrations中的 checksum 与修改过的迁移文件不匹配- 已删除的迁移记录残留在表中
注意:如果旧库状态已经完全正确(
prisma migrate status显示 up to date 且无 drift),全量导出也是可行的。关键是确保旧库本身没有历史遗留问题。
而由 Prisma 执行 migrate deploy 建表,能保证三者完全一致:
- 表结构 =
schema.prisma定义 _prisma_migrations= 迁移文件的忠实记录- checksum = 当前迁移文件的真实校验值
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!