Retro会上,大家提出了两个痛点:
- Dev 环境数据库部署经常失败:多分支并行,合并后 Alembic 迁移时间戳冲突,数据库拒绝执行
- AI 协作效率低:每次让 AI 生成代码,都要先用 MCP 读取数据库结构,费 token 又慢
Alembic 的两个坑
之前用 Alembic 管理数据库迁移:写 Python 迁移脚本,按版本号顺序执行。用了一年多,问题越来越明显。
多分支迁移被跳过
团队多分支并行开发。两个人同时改表结构:
# 开发者 A
migrations/20250210100000_add_avatar.sql
# 开发者 B
migrations/20250211090000_add_tags.sql如果 B 先合并,A 后合并:
20250211090000_add_tags.sql # ✅ 先执行
20250210100000_add_avatar.sql # ❌ 被跳过(时间戳更早)Alembic 检测到数据库已执行过 20250211...,拒绝执行更早的 20250210...。线上缺 avatar 字段,代码却在用,炸了。
AI 协作需要频繁读库
Alembic 没有统一 schema 文件,结构分散在几十个迁移脚本。每次让 AI 生成代码,都要先用 MCP 连数据库读表结构,等 2-3 秒,还消耗大量 token。
所以我们决定试试 Prisma。
Prisma 用 Schema 驱动
Prisma 的思路完全不同:Schema 驱动,不是迁移文件驱动。
核心文件只有一个 schema.prisma:
model User {
id String @id @default(cuid())
email String @unique
avatar String?
posts Post[]
}
model Post {
id String @id
title String
authorId String
author User @relation(fields: [authorId], references: [id])
}直接修改 schema,Prisma 自动生成迁移:
npx prisma migrate dev --name add-avatar
# 1. 对比 schema 和数据库,生成 SQL
# 2. 执行 SQL 更新数据库迁移冲突变成代码冲突
两个分支同时改 schema.prisma,Git 合并时会提示冲突。手动解决后重新生成迁移,Prisma 自动生成包含两个改动的 SQL。冲突在代码层面解决,不会延迟到部署时才发现。
单文件包含所有上下文
把 schema.prisma 直接喂给 AI,一次性获取所有表结构。AI 响应速度提升 3 倍,token 消耗减少 60%,字段名错误率从 30% 降到 5%。
另外,Prisma 自动生成 TypeScript 类型定义,不用再手动维护三份代码(Alembic 迁移 + TypeScript 类型 + ORM 配置)。
怎么用
核心命令
| 命令 | 场景 |
|---|---|
npx prisma migrate dev --name <描述> | 开发:修改 schema 后生成并应用迁移 |
npx prisma generate | 生成 TypeScript 类型定义 |
npx prisma migrate deploy | 生产:应用所有待执行的迁移 |
npx prisma migrate status | 检查数据库和 schema 是否同步 |
npx prisma db push | 快速原型:跳过迁移历史直接同步 |
npx prisma studio | 打开可视化数据库管理界面 |
客户端使用
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
// 类型安全的查询
await prisma.user.create({
data: { email: 'user@example.com', posts: { create: [{ title: 'Post' }] } }
});
await prisma.post.findMany({
where: { published: true },
include: { author: true }
});三个命令的区别
Prisma 用 _prisma_migrations 表跟踪迁移历史。给 User 表加 avatar 字段为例:
model User {
id String @id
email String @unique
avatar String? // 新字段
}migrate dev(开发环境)
npx prisma migrate dev --name add_avatar生成迁移文件 → 执行 SQL → 写 _prisma_migrations 表。Prisma 用 migration_name 和 checksum 判断是否执行过,与时间戳无关。多分支合并后,只要 migration_name 不同,都会执行。
migrate deploy(生产环境)
npx prisma migrate deploy检查 _prisma_migrations 表,找未执行的迁移,执行并写入记录。
db push(快速原型)
npx prisma db push直接同步 schema 到数据库,不生成迁移文件,不写 _prisma_migrations 表。适合快速原型,生产环境慎用。
Prisma 也有限制
数据库触发器和函数
Prisma 不支持。需要手动编辑迁移文件追加 SQL:
-- migrations/xxx/migration.sql
ALTER TABLE "User" ADD COLUMN "avatar" TEXT;
-- 手动追加
CREATE TRIGGER update_timestamp
BEFORE UPDATE ON "User"
FOR EACH ROW EXECUTE FUNCTION update_updated_at();复杂索引
// 简单索引可以
model User {
email String @unique
@@index([email, name])
}-- 部分索引、表达式索引需手动添加
CREATE INDEX idx_active_users ON users(email) WHERE deleted_at IS NULL;
CREATE INDEX idx_lower_email ON users(LOWER(email));数据库特定功能
- PostgreSQL ENUM 修改(Prisma 会重建整个 ENUM)
- 存储过程、视图、物化视图
- 分区表、继承表
解决方案:手动编辑生成的迁移文件,追加自定义逻辑。
代码回滚后怎么办
部署新功能后发现 bug,紧急回滚代码。但数据库已执行了迁移,出现不一致:
- 数据库:
_prisma_migrations有记录,avatar字段存在 - 代码:迁移文件丢失(回滚到之前的 commit)
快速修复:标记迁移已应用
npx prisma migrate resolve --applied 20250213_add_avatar_field告诉 Prisma “这个迁移已执行,虽然本地没文件,但不要再尝试”。系统继续运行,但数据库里多了个用不到的字段。适合数据库改动无害、不影响业务的情况。
开发/测试环境:重置数据库
npx prisma migrate reset # 删除所有数据,重新执行迁移
npx prisma db seed # 重新填充 seed 数据适合可以接受数据丢失的环境。
生产环境:新增回滚迁移
npx prisma migrate dev --name remove_avatarPrisma 生成删除字段的迁移:
-- migrations/20250214_remove_avatar/migration.sql
ALTER TABLE "User" DROP COLUMN "avatar";npx prisma migrate deploy保留完整迁移历史,可追溯,不丢失其他数据,可灰度发布。生产环境永远用新增迁移的方式”撤销”改动,保持迁移历史线性。
Prisma 完美解决了我们的问题:多分支合并不再有时间戳冲突导致部署失败,AI 直接读 schema.prisma 就能获取完整数据库上下文,不用再频繁调用 MCP 连库查表结构。当然,如果项目大量使用触发器、存储过程、物化视图这些数据库特定功能,还是老老实实用 Alembic。