在未运行 migrate reset 的情况下使用 Prisma 为现有数据库建立基线
了解为何 Prisma 会报告现有数据库出现偏移,为何“迁移重置”并非正确的解决方法,以及如何通过 db pull、migrate diff 和 migrate resolve 来建立基准。
在已经包含表和数据的数据库上使用 Prisma Migrate 时,很可能会出现这样的情况:首次执行 npx prisma migrate dev 时会因结构不一致而触发警告,并提示重置所有内容。这个提示意味着数据库中存在迁移历史记录所不知道的结构。如果选择接受该建议,将会删除你的数据。本指南将解释为何会出现这种冲突、为什么对于重要的数据库来说几乎 never 应该选择重置,以及如何为现有架构建立基准,让 Prisma 将其视为起点并仅从该点开始应用变更。
为何 Prisma 会检测到冲突
Prisma Migrate 会保存两份关于模式演变的记录:一份是位于 prisma/migrations 目录中的迁移文件,另一份则是数据库中名为 _prisma_migrations 的表,用于记录哪些迁移文件已被应用。当运行 migrate dev 时,Prisma 会针对临时数据库重放迁移历史,并将结果与真实数据库进行比较。
如果真实数据库中存在某些迁移文件并未创建的表,比如是因为手动创建、由其他工具生成或来自应用程序的早期版本,那么这两个数据库就会不一致。Prisma 将这种情况称为“漂移”。由于 migrate dev 是一个开发命令,它的默认处理方式是清空数据库并根据迁移历史重新构建它,因此会建议进行重置。对于仅用于开发的本地数据库来说这很合理,但对于其他任何用途的数据库而言则具有破坏性。
如需了解阴影数据库如何参与此对比的更多背景信息,请参阅Prisma的阴影数据库与命名冲突问题。
为何migrate reset不是正确的解决方案
npx prisma migrate reset会删除数据库或其架构中的所有表,然后根据迁移文件重新创建它们并运行初始化脚本。所有现有数据都会丢失。对于包含真实用户、订单或内容的数据库而言,这并非冲突解决方式,而是数据丢失。
更好的方法是不要改动数据库,而是让Prisma对数据的认知与实际数据保持一致。具体做法是将当前结构记录为第一个迁移文件,并告知Prisma该迁移已存在。
逐步建立基准
步骤1:分析现有数据库
运行 npx prisma db pull。Prisma会连接到数据库,读取其中的表、列、索引及关联关系,并将对应的模型写入 schema.prisma 文件中。完成此步骤后,该架构文件就能准确描述数据库的当前状态。
步骤2:生成基准迁移文件但不立即应用
为基准版本创建一个文件夹,例如 prisma/migrations/0_init。0_ 前缀可确保该文件在后续按时间戳排序的迁移文件之前被处理。接着使用 npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql 生成用于从空状态构建当前数据结构的 SQL 语句并保存到该文件夹中。在较新版本的 Prisma 中,目标参数的名称可能改为 --to-schema,请根据您的版本查看 npx prisma migrate diff --help 的说明。
这一步很重要,因为它能在不修改数据库的情况下生成迁移文件。常见的错误是在此阶段运行 npx prisma migrate dev --name baseline。对于已经存在表且没有迁移历史的数据库,该命令会检测到与之前相同的差异并要求再次重置,而这正是你想要避免的。由于现有数据库中已经存在表,因此绝不能对它执行基准 SQL。
步骤 3:标记基准状态为已应用
运行 npx prisma migrate resolve --applied 0_init。该参数即为迁移文件夹的名称。如果文件夹是用时间戳命名的,例如 20250101120000_baseline,则必须使用完整的名称,而不仅仅是 baseline。
此命令不会对您的表执行任何 SQL 操作。它会在 _prisma_migrations 中插入一行,表明基准版本已应用。实际上您是在修改 Prisma 的记录机制,使其将当前结构视为有意且有效的。
此方法如何解决冲突
这类似于 Git 的合并冲突:当您的分支缺少主分支已有的提交时,您会更新分支而非删除主分支。此处数据库版本更新,因此需要让 Prisma 的历史记录与数据库保持一致。
基准版本记录之后,下一次执行 npx prisma migrate dev 时,会在影子数据库中重新运行 0_init,从而获得与真实数据库相同的结构,并检测到没有差异。此后,每当您修改 schema.prisma 时,Prisma 会生成仅包含差异的新迁移文件,并仅应用这些差异。
为何这对大型数据库至关重要
当数据库存储大量数据时,建立基线才能发挥最大作用。由于基线已记录在_prisma_migrations中,且架构与实际数据库一致,Prisma会保留现有的表和行,仅应用新的更改,这样你的users表就能保持完整。
请记住以下几点:
- 针对每个现有环境(如测试环境和生产环境),各运行一次
migrate resolve --applied,因为每个数据库都有独立的_prisma_migrations表。 - 在生产环境中,应使用
npx prisma migrate deploy来应用后续的迁移,而非仅适用于开发环境的migrate dev。 - 将基线文件夹提交到版本控制系统中,以便所有开发人员和CI任务都能使用相同的起点。
核心要点
- 现有数据库出现漂移警告意味着 Prisma 的迁移历史记录缺失,而非数据库本身有误。
- 重置操作会删除数据;请将其视为仅适用于一次性本地数据库的工具。
- 可通过
db pull进行分析来确定基准,使用migrate diff --from-empty生成 SQL 语句,再通过migrate resolve --applied进行记录。 - 切勿在已包含数据的数据库上使用
migrate dev来创建基准,因为这会触发相同的重置提示。 - 建立基准后,Prisma 仅管理增量变更,现有数据不会被修改。