主题
Prisma 迁移管理示例
这个目录包含 Prisma 迁移的最佳实践和常见场景。
📋 迁移基本流程
1. 初始化迁移
bash
# 创建第一个迁移
npx prisma migrate dev --name init
# 这会:
# ✅ 根据 schema.prisma 生成 SQL 迁移脚本
# ✅ 执行迁移到数据库
# ✅ 生成 Prisma Client
# ✅ 创建 migrations/ 文件夹2. 添加新字段
prisma
// schema.prisma - 修改前
model User {
id Int @id @default(autoincrement())
name String
}
// schema.prisma - 修改后
model User {
id Int @id @default(autoincrement())
name String
email String @unique // ✅ 新增字段
}bash
# 创建迁移
npx prisma migrate dev --name add_user_email
# Prisma 会检测到变化并生成:
# migrations/20240201100000_add_user_email/migration.sql生成的 SQL:
sql
-- migrations/20240201100000_add_user_email/migration.sql
ALTER TABLE "User" ADD COLUMN "email" TEXT NOT NULL;
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");3. 处理已有数据
如果表中已有数据,添加 NOT NULL 字段会失败:
bash
# 方案 1: 提供默认值
npx prisma migrate dev --name add_email
# Prisma 会询问: "表中已有数据,新字段是 NOT NULL,如何处理?"
# 选择: 提供默认值(如 'default@example.com')sql
-- 生成的 SQL(带默认值)
ALTER TABLE "User" ADD COLUMN "email" TEXT NOT NULL DEFAULT 'default@example.com';
-- 后续可以手动更新这些默认值bash
# 方案 2: 先创建可选字段,再改为必填prisma
// 第一步: 先创建可选字段
model User {
email String? // 可选
}bash
npx prisma migrate dev --name add_email_optionalsql
-- 迁移 1
ALTER TABLE "User" ADD COLUMN "email" TEXT;然后手动填充数据后:
prisma
// 第二步: 改为必填
model User {
email String @unique // 必填
}bash
npx prisma migrate dev --name make_email_requiredsql
-- 迁移 2
UPDATE "User" SET "email" = 'user' || "id" || '@example.com' WHERE "email" IS NULL;
ALTER TABLE "User" ALTER COLUMN "email" SET NOT NULL;
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");🔄 常见迁移场景
场景 1: 重命名字段(保留数据)
❌ 错误做法(会丢失数据):
prisma
// 从
model User {
name String
}
// 改为
model User {
fullName String
}bash
npx prisma migrate dev --name rename_namesql
-- ❌ Prisma 默认生成的(会丢数据)
ALTER TABLE "User" DROP COLUMN "name";
ALTER TABLE "User" ADD COLUMN "fullName" TEXT NOT NULL;✅ 正确做法(保留数据):
bash
# 1. 生成迁移但不执行
npx prisma migrate dev --name rename_name_to_fullname --create-only
# 2. 手动编辑生成的 migration.sqlsql
-- 手动编辑: migrations/xxx_rename_name_to_fullname/migration.sql
-- ✅ 正确的 SQL(保留数据)
ALTER TABLE "User" RENAME COLUMN "name" TO "fullName";bash
# 3. 执行迁移
npx prisma migrate dev场景 2: 修改字段类型
prisma
// 从 String 改为 Int
model User {
age String // 改前
}
model User {
age Int // 改后
}sql
-- 生成的 SQL
ALTER TABLE "User" ALTER COLUMN "age" TYPE INTEGER USING "age"::INTEGER;注意: 如果数据不能转换(如 "abc" → Int),迁移会失败!
场景 3: 添加外键关系
prisma
// 添加前
model Post {
id Int @id
title String
}
// 添加后
model Post {
id Int @id
title String
authorId Int
author User @relation(fields: [authorId], references: [id])
}
model User {
id Int @id
posts Post[]
}sql
-- 生成的 SQL
ALTER TABLE "Post" ADD COLUMN "authorId" INTEGER NOT NULL;
ALTER TABLE "Post" ADD CONSTRAINT "Post_authorId_fkey"
FOREIGN KEY ("authorId") REFERENCES "User"("id")
ON DELETE RESTRICT ON UPDATE CASCADE;
CREATE INDEX "Post_authorId_idx" ON "Post"("authorId");场景 4: 添加索引
prisma
model Post {
id Int @id
title String
published Boolean
createdAt DateTime @default(now())
@@index([published]) // 单字段索引
@@index([createdAt]) // 单字段索引
@@index([published, createdAt]) // 复合索引
}sql
-- 生成的 SQL
CREATE INDEX "Post_published_idx" ON "Post"("published");
CREATE INDEX "Post_createdAt_idx" ON "Post"("createdAt");
CREATE INDEX "Post_published_createdAt_idx" ON "Post"("published", "createdAt");🚀 生产环境迁移
开发环境 vs 生产环境
bash
# ❌ 开发环境:使用 migrate dev(会重置数据库)
npx prisma migrate dev --name add_feature
# ✅ 生产环境:使用 migrate deploy(只应用未执行的迁移)
npx prisma migrate deploy生产部署流程
yaml
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
# ✅ 关键步骤:运行迁移
- name: Run database migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Build application
run: npm run build
- name: Deploy to Vercel
run: vercel --prod
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}迁移的幂等性
Prisma 通过 _prisma_migrations 表确保迁移的幂等性:
sql
-- Prisma 自动创建的迁移记录表
CREATE TABLE "_prisma_migrations" (
id VARCHAR(36) PRIMARY KEY,
checksum VARCHAR(64) NOT NULL,
finished_at TIMESTAMP,
migration_name VARCHAR(255) NOT NULL,
logs TEXT,
rolled_back_at TIMESTAMP,
started_at TIMESTAMP DEFAULT now(),
applied_steps_count INTEGER DEFAULT 0
);工作原理:
- 运行
migrate deploy时,Prisma 扫描migrations/文件夹 - 计算每个迁移文件的 checksum
- 查询
_prisma_migrations表,检查哪些已执行 - 只执行未记录的迁移
- 执行成功后,记录到
_prisma_migrations
好处: 多次运行 migrate deploy 是安全的,不会重复执行。
⚠️ 破坏性变更处理
删除列(会丢失数据)
prisma
// 删除 bio 字段
model Profile {
id Int @id
// bio String // ❌ 删除这一行
avatar String
}bash
# 1. 创建迁移(先不执行)
npx prisma migrate dev --name remove_bio --create-only
# 2. 审查生成的 SQL
cat migrations/xxx_remove_bio/migration.sqlsql
-- 生成的 SQL
ALTER TABLE "Profile" DROP COLUMN "bio";bash
# 3. 确认数据不需要后,执行迁移
npx prisma migrate dev最佳实践: 在删除列之前,先在生产环境验证这个列确实不再使用。
删除表(会丢失数据)
bash
# 1. 生成迁移但不执行
npx prisma migrate dev --name remove_old_table --create-only
# 2. 手动审查 SQL
# 3. 备份数据
pg_dump -t old_table mydb > old_table_backup.sql
# 4. 执行迁移
npx prisma migrate dev🔧 迁移工具命令
bash
# 创建并应用迁移(开发环境)
npx prisma migrate dev --name my_migration
# 只创建迁移文件,不执行(用于手动编辑)
npx prisma migrate dev --name my_migration --create-only
# 应用迁移(生产环境)
npx prisma migrate deploy
# 重置数据库(⚠️ 会删除所有数据)
npx prisma migrate reset
# 查看迁移状态
npx prisma migrate status
# 解决迁移冲突
npx prisma migrate resolve --applied <migration_name>
npx prisma migrate resolve --rolled-back <migration_name>📚 团队协作最佳实践
1. 提交迁移到 Git
bash
# ✅ 务必提交 migrations/ 文件夹
git add prisma/migrations/
git add prisma/schema.prisma
git commit -m "feat: add user email field"
git push2. 拉取代码后同步数据库
bash
git pull
# 应用团队成员的迁移
npx prisma migrate dev3. 解决迁移冲突
如果两个开发者同时创建迁移:
bash
# 开发者 A 创建迁移
npx prisma migrate dev --name add_field_a
# 开发者 B 创建迁移
npx prisma migrate dev --name add_field_b
# 合并时,migrations/ 文件夹会冲突解决方案:
- 保留两个迁移文件
- 重新运行
npx prisma migrate dev - Prisma 会按时间戳顺序应用两个迁移
🎯 总结
✅ 最佳实践
- 每次 Schema 变更都创建迁移 — 不要手动修改数据库
- 使用
--create-only审查 SQL — 重命名、删除操作需要人工确认 - 提交迁移到 Git — 团队协作必须同步迁移
- 生产环境用
migrate deploy— 不要用migrate dev - 备份数据 — 执行破坏性变更前先备份
❌ 常见错误
- 不要手动编辑 schema.prisma 后直接
prisma generate— 必须先migrate dev - 不要删除 migrations/ 文件夹 — 这是数据库历史记录
- 不要在生产环境用
migrate dev— 会重置数据库 - 不要修改已提交的迁移 — 会导致 checksum 不匹配
📖 相关章节
- Schema 示例 - 如何设计 Prisma Schema
- 查询示例 - 如何使用生成的 Prisma Client
- Next.js 集成 - 在 Next.js 中使用 Prisma