Chapter 10

部署与工程化实战

把 Prisma 项目稳妥推上生产:迁移策略、种子数据、性能调优、日志监控、Docker 化,附一份上线 checklist。

10.1 核心概念

生产迁移策略
CI/CD 里以 migrate deploy 顺序应用迁移,与应用发布解耦、幂等、不重置库。破坏性变更走展开-收缩两步发布。
seeding(种子数据)
用脚本填充初始/示例数据(角色、字典表、演示账号)。在 package.jsonprisma.seed 配置,migrate reset/db seed 时执行。
N+1 查询
取 N 条主记录后又逐条查关系 = 1+N 次往返。用 include/嵌套 select 合并,或批量 in 查询消除。
索引(index)
schema 里用 @@index([...]) / @unique 声明。为高频 where/orderBy/join 列建索引,是最见效的调优手段。
查询日志与 metrics
new PrismaClient({ log: ["query"] }) 打印 SQL 与耗时;$metrics / 事件监听可接入 Prometheus 观测慢查询与连接池。
binaryTargets
schema 的 generator 里声明目标平台(如 debian-openssl-3.0.x),确保 Docker/Lambda 里生成对应查询引擎二进制。
部署 checklist
上线前逐项核对:迁移、连接串、连接池、日志、备份、回滚方案,减少事故。

10.2 CI/CD 迁移流水线

# 部署脚本(发布前执行,幂等)
$ npx prisma migrate deploy
$ npx prisma generate           # 若构建阶段未生成
$ node dist/server.js
# GitHub Actions 片段
- name: Apply migrations
  run: npx prisma migrate deploy
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}
迁移与发布顺序

加列/建表等向后兼容变更可先迁移再发新代码;删列/改类型等破坏性变更须先发能兼容新旧的代码,再迁移,避免滚动发布期间旧实例访问已删列而报错。

10.3 Seeding

// prisma/seed.ts
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();

async function main() {
  await prisma.role.createMany({
    data: [{ name: "ADMIN" }, { name: "USER" }],
    skipDuplicates: true,
  });
}
main().finally(() => prisma.$disconnect());
// package.json
{
  "prisma": { "seed": "tsx prisma/seed.ts" }
}
# 运行:npx prisma db seed

10.4 性能调优:消除 N+1

// ❌ N+1:循环里逐条查关系
const users = await prisma.user.findMany();
for (const u of users) {
  const posts = await prisma.post.findMany({ where: { authorId: u.id } });
}

// ✅ 一次带出关系
const withPosts = await prisma.user.findMany({
  include: { posts: true },
});

10.5 索引与慢查询

model Post {
  id        Int      @id @default(autoincrement())
  authorId  Int
  published Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([authorId])                 // 外键 join 常查
  @@index([published, createdAt])      // 列表页复合索引
}
看真实 SQL

log: ["query"] 观察 Prisma 生成的 SQL 与耗时,再用数据库的 EXPLAIN ANALYZE 定位缺索引/全表扫描。别凭感觉加索引——按实际慢查询加。

10.6 日志与监控

const prisma = new PrismaClient({
  log: [{ emit: "event", level: "query" }, "warn", "error"],
});
prisma.$on("query", (e) => {
  if (e.duration > 200) console.warn("slow query", e.duration, e.query);
});

10.7 Docker 多阶段构建

# 生成阶段:装依赖并 prisma generate
FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx prisma generate && npm run build

# 运行阶段
FROM node:20-slim
RUN apt-get update -y && apt-get install -y openssl   # 引擎依赖
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/prisma ./prisma
CMD ["sh", "-c", "npx prisma migrate deploy && node dist/server.js"]
binaryTargets 与 openssl

Prisma 查询引擎依赖 OpenSSL,Alpine/slim 镜像常缺。装好 openssl,并在 generator 里设 binaryTargets = ["native", "debian-openssl-3.0.x"],否则运行时报"引擎找不到/不兼容"。

10.8 上线 Checklist

10.9 结语

至此 10 章走完 Prisma 全景:从 schema.prisma 建模、关系、Migrate 迁移,到类型安全 CRUD、进阶查询、事务与原始 SQL、$extends 扩展、框架集成,直到生产部署。Prisma 的价值在于把"数据模型"变成团队共享、可 review、类型驱动的单一事实来源。若你更看重轻量与贴近 SQL、需要上 Edge,别忘了同站的 Drizzle ORM 完全指南——两把利器,因项目而择。祝你建模愉快。