修补与热修复
修补或热修复数据库涉及直接在生产环境中进行通常是时间敏感的更改。例如,你可能会直接在生产数据库中添加索引,以解决运行缓慢的查询问题。
直接修补生产数据库会导致**模式漂移**:你的数据库模式已“偏离”了真相来源,并与你的迁移历史记录不同步。你可以使用 `prisma migrate resolve` 命令来协调你的迁移历史记录,*而无需*使用 `prisma migrate deploy` 命令来移除和重新应用热修复。
通过修补或热修复协调你的迁移历史
以下场景假设你已在生产环境中进行了手动更改,并希望将该更改传播到你的迁移历史记录和其他数据库。
在生产环境中协调你的迁移历史和数据库模式
-
在 schema 中复制你在生产环境中进行的更改——例如,向特定模型添加
@@index
。 -
生成一个新的迁移,并记下完整的迁移名称,包括时间戳,该名称会写入 CLI 中:(
20210316150542_retroactively_add_index
)npx prisma migrate dev --name retroactively-add-index
显示CLI结果migrations/
└─ 20210316150542_retroactively_add_index/
└─ migration.sql
Your database is now in sync with your schema.
✔ Generated Prisma Client (2.19.0-dev.29) to .\node_modules\@prisma\client in 190ms -
将迁移推送到生产环境,**但不要运行 `migrate deploy`**。相反,将上一步中创建的迁移标记为“已应用”,这样 Prisma Migrate 就不会尝试第二次应用你的热修复。
prisma migrate resolve --applied "20201127134938-retroactively-add-index"
此命令会将迁移添加到迁移历史记录表中,而不会运行实际的 SQL。
-
对其他已修补的数据库重复上一步骤——例如,如果你已将补丁应用到暂存数据库。
-
将迁移传播到其他未修补的数据库——例如,通过将迁移提交到源代码管理并允许你的 CI/CD 流水线将其应用到所有数据库。
**注意**:迁移不会应用于已被 `prisma migrate resolve` 命令标记为已应用的数据库。
失败的迁移
迁移可能会失败,如果
- 你在运行迁移之前修改了迁移并引入了语法错误
- 你向已包含数据的表添加了强制(
NOT NULL
)列 - 迁移过程意外停止
- 数据库在迁移过程中关闭
_prisma_migrations
表中的每个迁移都有一个 logs
列,用于存储错误。
在生产环境中处理失败的迁移有两种方法
- 回滚,可选地修复问题,并重新部署
- 手动完成迁移步骤并解决迁移问题
选项 1:将迁移标记为已回滚并重新部署
以下示例演示了如何回滚迁移,可选地进行更改以修复问题,并重新部署
-
将迁移标记为已回滚——这会更新
_prisma_migrations
表中的迁移记录,将其注册为已回滚,从而允许再次应用它prisma migrate resolve --rolled-back "20201127134938_added_bio_index"
-
如果迁移部分运行,你可以选择
- 修改迁移以检查某一步骤是否已完成(例如:
CREATE TABLE ... IF NOT EXISTS
)或者 - 手动还原已完成的步骤(例如,删除已创建的表)
如果你修改了迁移,请确保将其复制回源代码管理,以确保你的生产数据库状态在开发中得到精确反映。
- 修改迁移以检查某一步骤是否已完成(例如:
-
修复失败迁移的根本原因(如果相关)——例如,如果迁移由于 SQL 脚本本身的问题而失败。请确保将任何更改的迁移复制回源代码管理。
-
重新部署迁移
prisma migrate deploy
选项 2:手动完成迁移并解决为已应用
以下示例演示了如何手动完成迁移的步骤并将其标记为已应用。
-
在生产数据库上手动完成迁移步骤。确保任何手动步骤与迁移文件中的步骤完全匹配,并将任何更改复制回源代码管理。
-
将迁移解决为已应用——这会告诉 Prisma Migrate 认为该迁移已成功应用
prisma migrate resolve --applied "20201127134938_my_migration"
使用 migrate diff
和 db execute
修复失败的迁移
为帮助修复失败的迁移,Prisma ORM 提供了以下用于创建和执行迁移文件的命令
prisma migrate diff
用于对比两个数据库模式源,以创建一个将一个模式转换为第二个模式状态的迁移。你可以输出差异摘要或 SQL 脚本。该脚本可以通过> file_name.sql
输出到文件,或者通过管道传输到db execute --stdin
命令。prisma db execute
将 SQL 脚本应用到数据库,而不与 Prisma 迁移表交互。
这些命令在 3.9.0
及更高版本中以预览版形式提供(带有 --preview-feature
CLI 标志),并在 3.13.0
及更高版本中普遍可用。
本节提供了一个失败迁移的示例场景,并解释了如何使用 migrate diff
和 db execute
来修复它。
失败迁移的示例
假设你在本地开发环境和生产环境中,你的 schema 中都有以下 User
模型
model User {
id Int @id
name String
}
此时,你的 schema 是同步的,但两个环境中的数据是不同的。
然后你决定更改你的数据模型,添加另一个 Post
模型并将 User
上的 name
字段设置为唯一
model User {
id Int @id
name String @unique
email String?
}
model Post {
id Int @id
title String
}
你使用命令 prisma migrate dev -n Unique
创建了一个名为 'Unique' 的迁移,该迁移保存在你的本地迁移历史记录中。在开发环境中应用该迁移成功,现在是时候发布到生产环境了。
不幸的是,此迁移只能部分执行。创建 Post
模型和添加 email
列成功,但使 name
字段唯一失败,并出现以下错误
ERROR 1062 (23000): Duplicate entry 'paul' for key 'User_name_key'
这是因为你的生产数据库中存在非唯一数据(例如,两个用户具有相同的名称)。
你现在需要手动从部分执行的迁移中恢复。在你从失败状态恢复之前,使用 prisma migrate deploy
的进一步迁移是不可能的。
此时有两种选择,具体取决于你如何处理非唯一数据
- 你意识到非唯一数据是有效的,并且你无法继续当前的开发工作。你想回滚整个迁移。要做到这一点,请参阅回滚并撤消所有更改
- 数据库中存在非唯一数据是无意的,你想修复它。修复后,你想继续进行其余的迁移。要做到这一点,请参阅前进并应用缺失的更改
回滚并撤消所有更改
在这种情况下,你需要创建一个迁移,将你的生产数据库恢复到上次迁移之前的数据模型状态。
-
首先,你需要失败迁移之前的迁移历史记录。你可以从 Git 历史记录中获取,或者在本地删除迁移历史记录中最后一次失败迁移的文件夹。
-
你现在想将你的生产环境从当前的失败状态恢复到你的本地迁移历史记录中指定的状态
-
运行以下
prisma migrate diff
命令npx prisma migrate diff \
--from-url "$DATABASE_URL_PROD" \
--to-migrations ./prisma/migrations \
--shadow-database-url $SHADOW_DATABASE_URL \
--script > backward.sql这将创建一个 SQL 脚本文件,其中包含将你的生产环境从当前失败状态恢复到你的迁移历史记录定义的目标状态所需的所有更改。请注意,由于我们正在使用
--to-migrations
,该命令需要一个影子数据库。 -
运行以下
prisma db execute
命令npx prisma db execute --url "$DATABASE_URL_PROD" --file backward.sql
这会将 SQL 脚本中的更改应用到目标数据库,而不与迁移表交互。
-
运行以下
prisma migrate resolve
命令npx prisma migrate resolve --rolled-back Unique
这会将生产环境中迁移表中名为 'Unique' 的失败迁移标记为已回滚。
-
你的本地迁移历史记录现在与你的生产数据库的状态一致。你现在可以再次修改数据模型,以创建符合你对正在处理的功能(带有非唯一名称)的新理解的迁移。
前进并应用缺失的更改
在这种情况下,你需要修复非唯一数据,然后按计划继续进行其余的迁移
-
尝试将迁移部署到生产环境时出现的错误消息已经告诉你
name
列中存在重复数据。你需要修改或删除有问题的行。 -
继续应用失败迁移的其余部分,以达到在你的
schema.prisma
文件中定义的数据模型-
运行以下
prisma migrate diff
命令
npx prisma migrate diff --from-url "$DATABASE_URL_PROD" --to-schema-datamodel schema.prisma --script > forward.sql这将创建一个 SQL 脚本文件,其中包含将你的生产环境从当前失败状态转换为你的
schema.prisma
文件中定义的目标状态所需的所有更改。 -
运行以下
prisma db execute
命令npx prisma db execute --url "$DATABASE_URL_PROD" --file forward.sql
这会将 SQL 脚本中的更改应用到目标数据库,而不与迁移表交互。
-
运行以下
prisma migrate resolve
命令npx prisma migrate resolve --applied Unique
这会将生产环境中迁移表中名为 'Unique' 的失败迁移标记为已应用。
-
你的本地迁移历史记录现在与你的生产环境的状态一致。你现在可以继续使用已知的 migrate dev
/migrate deploy
工作流程。
迁移历史冲突
这不适用于 3.12.0 及更高版本。
如果已应用的迁移已被编辑,prisma migrate deploy
会发出警告——但是,它不会停止迁移过程。要消除警告,请从源代码管理中恢复原始迁移。
Prisma Migrate 和 PgBouncer
如果你尝试在使用 PgBouncer 进行连接池的环境中运行 Prisma Migrate 命令,你可能会看到以下错误
Error: undefined: Database error
Error querying the database: db error: ERROR: prepared statement "s0" already exists
有关更多信息和解决方法,请参阅Prisma Migrate 和 PgBouncer 解决方法。关注 GitHub 问题 #6485 以获取更新。