部署上线
数据库迁移
安全地迁移和更新数据库结构
数据库迁移是管理数据库结构变更的过程,确保开发、测试、生产环境的数据库结构一致。
为什么需要数据库迁移
- 版本控制:数据库结构也需要版本管理
- 团队协作:确保所有人的数据库结构一致
- 安全部署:避免手动修改数据库导致错误
- 可回滚:出问题时可以回退
迁移工具
Prisma(推荐)
优点:
- 类型安全
- 自动生成迁移文件
- 支持多种数据库
- 开发体验好
Drizzle ORM
优点:
- 轻量级
- SQL-like API
- 性能好
Knex.js
优点:
- 灵活
- 支持原生 SQL
- 成熟稳定
快速开始:Prisma
1. 安装
npm install prisma @prisma/client
npx prisma init2. 定义数据模型
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}3. 创建迁移
# 创建迁移文件
npx prisma migrate dev --name init
# Prisma 会:
# 1. 生成迁移 SQL
# 2. 执行迁移
# 3. 生成 Prisma Client4. 查看迁移文件
-- prisma/migrations/20240101000000_init/migration.sql
CREATE TABLE "User" (
"id" SERIAL PRIMARY KEY,
"email" TEXT NOT NULL UNIQUE,
"name" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE "Post" (
"id" SERIAL PRIMARY KEY,
"title" TEXT NOT NULL,
"content" TEXT,
"published" BOOLEAN NOT NULL DEFAULT false,
"authorId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"("id")
);5. 使用 Prisma Client
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
// 创建用户
const user = await prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
},
})
// 查询
const users = await prisma.user.findMany({
include: {
posts: true,
},
})生产环境部署
1. 在 CI/CD 中运行迁移
# .github/workflows/deploy.yml
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}2. 在部署平台运行
Vercel
在 package.json 中添加:
{
"scripts": {
"build": "prisma generate && prisma migrate deploy && next build"
}
}Railway
Railway 自动检测 Prisma,部署时自动运行迁移。
Fly.io
在 fly.toml 中:
[deploy]
release_command = "npx prisma migrate deploy"常见迁移场景
添加字段
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
// 新增字段
avatar String?
bio String?
createdAt DateTime @default(now())
}npx prisma migrate dev --name add_user_avatar_bio修改字段
model User {
id Int @id @default(autoincrement())
email String @unique
name String // 改为必填
createdAt DateTime @default(now())
}注意:如果已有数据,需要先填充默认值。
删除字段
model User {
id Int @id @default(autoincrement())
email String @unique
// 删除 name 字段
createdAt DateTime @default(now())
}npx prisma migrate dev --name remove_user_name重命名字段
Prisma 会检测为删除 + 添加,需要手动修改迁移文件:
-- 手动修改迁移文件
ALTER TABLE "User" RENAME COLUMN "name" TO "fullName";数据迁移
在迁移中修改数据
-- prisma/migrations/.../migration.sql
-- 添加字段
ALTER TABLE "User" ADD COLUMN "status" TEXT;
-- 填充默认值
UPDATE "User" SET "status" = 'active' WHERE "status" IS NULL;
-- 设置为必填
ALTER TABLE "User" ALTER COLUMN "status" SET NOT NULL;使用 Prisma 脚本
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// 迁移数据
const users = await prisma.user.findMany()
for (const user of users) {
await prisma.user.update({
where: { id: user.id },
data: {
status: 'active',
},
})
}
}
main()npx tsx prisma/seed.ts回滚迁移
Prisma
Prisma 不支持自动回滚,需要手动:
# 1. 查看迁移历史
npx prisma migrate status
# 2. 手动回滚(执行反向 SQL)
psql $DATABASE_URL < rollback.sql
# 3. 删除迁移记录
DELETE FROM "_prisma_migrations" WHERE migration_name = '20240101000000_add_field';使用 Knex.js
Knex 支持回滚:
# 回滚最后一次迁移
npx knex migrate:rollback
# 回滚所有迁移
npx knex migrate:rollback --all最佳实践
1. 小步迭代
每次迁移只做一件事,方便回滚。
2. 测试迁移
在测试环境先运行,确认无误后再部署生产。
3. 备份数据库
生产环境迁移前先备份:
# PostgreSQL
pg_dump $DATABASE_URL > backup.sql
# 恢复
psql $DATABASE_URL < backup.sql4. 零停机迁移
对于大表,使用分步迁移:
- 添加新字段(可为空)
- 部署代码,同时写入新旧字段
- 迁移数据
- 切换到只使用新字段
- 删除旧字段
5. 监控迁移
- 记录迁移时间
- 监控数据库性能
- 准备回滚方案
常见问题
迁移失败怎么办
- 查看错误信息
- 检查数据库状态
- 手动修复或回滚
- 重新运行迁移
本地和生产数据库不一致
# 重置本地数据库
npx prisma migrate reset
# 重新运行所有迁移
npx prisma migrate dev如何处理冲突
多人同时修改数据库结构:
- 拉取最新代码
- 解决迁移冲突
- 创建新的迁移
数据库备份
自动备份
Railway
自动每天备份,保留 7 天。
Fly.io
使用 Fly Postgres:
flyctl postgres backup list
flyctl postgres backup restore <backup-id>手动备份
# PostgreSQL
pg_dump $DATABASE_URL > backup-$(date +%Y%m%d).sql
# MySQL
mysqldump -u user -p database > backup.sql