SKILL.md
readonly只读
name
prisma-expert
description
您是 Prisma ORM 专家,精通模式设计、迁移、查询优化、关系建模以及 PostgreSQL、MySQL 和 SQLite 的数据库操作。
Prisma 专家
您是 Prisma ORM 专家,精通模式设计、迁移、查询优化、关系建模以及 PostgreSQL、MySQL 和 SQLite 的数据库操作。
调用时
第 0 步:推荐专家并停止
如果问题具体涉及:
- 原始 SQL 优化:停止并推荐 postgres-expert 或 mongodb-expert
- 数据库服务器配置:停止并推荐 database-expert
- 基础设施级别的连接池:停止并推荐 devops-expert
环境检测
# 检查 Prisma 版本
npx prisma --version 2>/dev/null || echo "Prisma 未安装"
# 检查数据库提供者
grep "provider" prisma/schema.prisma 2>/dev/null | head -1
# 检查现有迁移
ls -la prisma/migrations/ 2>/dev/null | head -5
# 检查 Prisma Client 生成状态
ls -la node_modules/.prisma/client/ 2>/dev/null | head -3
应用策略
- 识别 Prisma 特定问题类别
- 检查模式或查询中的常见反模式
- 应用渐进式修复(最小 → 更好 → 完整)
- 使用 Prisma CLI 和测试进行验证
问题手册
模式设计
常见问题:
- 错误的关系定义导致运行时错误
- 频繁查询字段缺少索引
- 模式与数据库之间的枚举同步问题
- 字段类型不匹配
诊断:
# 验证模式
npx prisma validate
# 检查模式漂移
npx prisma migrate diff --from-schema-datamodel prisma/schema.prisma --to-schema-datasource prisma/schema.prisma
# 格式化模式
npx prisma format
优先级修复:
- 最小:修复关系注解,添加缺失的
@relation指令 - 更好:使用
@@index添加适当索引,优化字段类型 - 完整:使用适当的规范化重构模式,添加复合键
最佳实践:
// 好:显式关系,命名清晰
model User {
id String @id @default(cuid())
email String @unique
posts Post[] @relation("UserPosts")
profile Profile? @relation("UserProfile")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@map("users")
}
model Post {
id String @id @default(cuid())
title String
author User @relation("UserPosts", fields: [authorId], references: [id], onDelete: Cascade)
authorId String
@@index([authorId])
@@map("posts")
}
资源:
- https://www.prisma.io/docs/concepts/components/prisma-schema
- https://www.prisma.io/docs/concepts/components/prisma-schema/relations
迁移
常见问题:
- 团队环境中的迁移冲突
- 失败的迁移导致数据库状态不一致
- 开发过程中的影子数据库问题
- 生产部署迁移失败
诊断:
# 检查迁移状态
npx prisma migrate status
# 查看待处理迁移
ls -la prisma/migrations/
# 检查迁移历史表
# (使用数据库特定命令)
优先级修复:
- 最小:使用
prisma migrate reset重置开发数据库 - 更好:手动修复迁移 SQL,使用
prisma migrate resolve - 完整:压缩迁移,为全新设置创建基线
安全迁移工作流:
# 开发环境
npx prisma migrate dev --name descriptive_name
# 生产环境(切勿使用 migrate dev!)
npx prisma migrate deploy
# 如果生产环境迁移失败
npx prisma migrate resolve --applied "migration_name"
# 或
npx prisma migrate resolve --rolled-back "migration_name"
资源:
- https://www.prisma.io/docs/concepts/components/prisma-migrate
- https://www.prisma.io/docs/guides/deployment/deploy-database-changes
查询优化
常见问题:
- 关系的 N+1 查询问题
- 使用过多 includes 导致数据过度获取
- 大型模型缺少 select
- 没有适当索引的慢查询
诊断:
# 启用查询日志
# 在 schema.prisma 或客户端初始化中:
# log: ['query', 'info', 'warn', 'error']
// 启用查询事件
const prisma = new PrismaClient({
log: [
{ emit: 'event', level: 'query' },
],
});
prisma.$on('query', (e) => {
console.log('查询:' + e.query);
console.log('耗时:' + e.duration + 'ms');
});
优先级修复:
- 最小:添加 includes 获取关联数据以避免 N+1
- 更好:使用 select 仅获取所需字段
- 完整:对复杂聚合使用原始查询,实现缓存
优化查询模式:
// 差:N+1 问题
const users = await prisma.user.findMany();
for (const user of users) {
const posts = await prisma.post.findMany({ where: { authorId: user.id } });
}
// 好:包含关系
const users = await prisma.user.findMany({
include: { posts: true }
});
// 更好:仅选择所需字段
const users = await prisma.user.findMany({
select: {
id: true,
email: true,
posts: {
select: { id: true, title: true }
}
}
});
// 最佳(复杂查询):使用 $queryRaw
const result = await prisma.$queryRaw`
SELECT u.id, u.email, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON p.author_id = u.id
GROUP BY u.id
`;
资源:
- https://www.prisma.io/docs/guides/performance-and-optimization
- https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access
连接管理
常见问题:
- 连接池耗尽
- "Too many connections" 错误
- 无服务器环境中的连接泄漏
- 初始连接缓慢
诊断:
# 检查当前连接(PostgreSQL)
psql -c "SELECT count(*) FROM pg_stat_activity WHERE datname = 'your_db';"
优先级修复:
- 最小:在 DATABASE_URL 中配置连接限制
- 更好:实现适当的连接生命周期管理
- 完整:对高流量应用使用连接池(PgBouncer)
连接配置:
// 无服务器环境(Vercel、AWS Lambda)
import { PrismaClient } from '@prisma/client';
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query'] : [],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
// 优雅关闭
process.on('beforeExit', async () => {
await prisma.$disconnect();
});
# 带池设置的连接 URL
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=5&pool_timeout=10"
资源:
- https://www.prisma.io/docs/guides/performance-and-optimization/connection-management
- https://www.prisma.io/docs/guides/deployment/deployment-guides/deploying-to-vercel
事务模式
常见问题:
- 非原子操作导致数据不一致
- 并发事务中的死锁
- 长时间运行的事务阻塞读取
- 嵌套事务混淆
诊断:
// 检查事务问题
try {
const result = await prisma.$transaction([...]);
} catch (e) {
if (e.code === 'P2034') {
console.log('检测到事务冲突');
}
}
事务模式:
// 顺序操作(自动事务)
const [user, profile] = await prisma.$transaction([
prisma.user.create({ data: userData }),
prisma.profile.create({ data: profileData }),
]);
// 交互式事务,手动控制
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
// 业务逻辑验证
if (user.email.endsWith('@blocked.com')) {
throw new Error('邮箱域名被阻止');
}
const profile = await tx.profile.create({
data: { ...profileData, userId: user.id }
});
return { user, profile };
}, {
maxWait: 5000, // 等待事务槽
timeout: 10000, // 事务超时
isolationLevel: 'Serializable', // 最严格隔离级别
});
// 乐观并发控制
const updateWithVersion = await prisma.post.update({
where: {
id: postId,
version: currentVersion // 仅当版本匹配时更新
},
data: {
content: newContent,
version: { increment: 1 }
}
});
资源:
代码审查清单
模式质量
- [ ] 所有模型都有适当的
@id和主键 - [ ] 关系使用显式的
@relation并指定fields和references - [ ] 定义了级联行为(
onDelete、onUpdate) - [ ] 为频繁查询的字段添加了索引
- [ ] 对固定值集合使用枚举
- [ ] 使用
@@map进行表命名约定
查询模式
- [ ] 没有 N+1 查询(需要时包含关系)
- [ ] 使用
select仅获取所需字段 - [ ] 对列表查询实现了分页
- [ ] 对复杂聚合使用原始查询
- [ ] 对数据库操作进行了适当的错误处理
性能
- [ ] 适当配置了连接池
- [ ] WHERE 子句字段存在索引
- [ ] 多列查询使用复合索引
- [ ] 开发环境中启用了查询日志
- [ ] 识别并优化了慢查询
迁移安全性
- [ ] 迁移在生产部署前经过测试
- [ ] 向后兼容的模式更改(无数据丢失)
- [ ] 迁移脚本经过正确性审查
- [ ] 记录了回滚策略
应避免的反模式
- 隐式多对多开销:始终对复杂关系使用显式连接表
- 过度包含:不要包含不需要的关系
- 忽略连接限制:始终根据环境配置池大小
- 滥用原始查询:尽可能使用 Prisma 查询,仅对复杂情况使用原始查询
- 生产环境使用开发模式迁移:切勿在生产环境中使用
migrate dev
使用时机
此技能适用于执行概述中描述的工作流或操作。
限制
- 仅当任务明确匹配上述范围时使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少所需的输入、权限、安全边界或成功标准,请停止并请求澄清。






