从零搭建 SQL 数据库到 ORM 选型:Prisma 与 Drizzle 深度对比

从零搭建 SQL 数据库到 ORM 选型:Prisma 与 Drizzle 深度对比

在 TypeScript 全栈开发中,数据库层是绕不开的一环。很多团队在选型时会陷入一个误区------纠结"哪个 ORM 更好"。但真正需要回答的问题是:你的团队愿意接受哪种类型的构建期成本? Prisma 把 schema 压缩成自定义 DSL 和一个代码生成步骤;Drizzle 把一切保留在 TypeScript 模块中,通过结构推断类型。

这篇文章分为两大部分:第一部分从零搭建一个 SQL 数据库 ,覆盖 Docker 环境、基础 SQL、数据表设计;第二部分深入对比 Prisma 和 Drizzle 这两个主流 ORM,从架构、类型系统、迁移工作流、性能到边缘运行时支持,帮你做出有据可依的选型决策。

第一部分:SQL 数据库搭建与数据表管理

1.1 用 Docker 搭建本地 PostgreSQL

本地直接安装数据库容易污染系统环境,推荐用 Docker Compose 管理。新建 docker-compose.yml:

yaml 复制代码
version: '3.8'
services:
  postgres:
    image: postgres:16
    container_name: dev-postgres
    restart: always
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev123
      POSTGRES_DB: myapp
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

启动并连接:

bash 复制代码
docker compose up -d
psql -h localhost -U dev -d myapp

pgdata 卷保证了数据持久化,删除容器不会丢失数据。需要重置时,直接删除卷即可。

1.2 基础 SQL:从建表到查询

在引入 ORM 之前,先用原生 SQL 建立对数据库的直观理解。以下是一个典型的博客系统表结构:

sql 复制代码
-- 用户表
CREATE TABLE users (
  id        SERIAL PRIMARY KEY,
  email     VARCHAR(255) UNIQUE NOT NULL,
  name      VARCHAR(100),
  created_at TIMESTAMP DEFAULT NOW()
);

-- 文章表
CREATE TABLE posts (
  id         SERIAL PRIMARY KEY,
  title      VARCHAR(255) NOT NULL,
  content    TEXT,
  published  BOOLEAN DEFAULT FALSE,
  author_id  INTEGER REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW()
);

-- 为常用查询字段建立索引
CREATE INDEX idx_posts_author ON posts(author_id);
CREATE INDEX idx_users_email ON users(email);

插入和查询:

sql 复制代码
INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice');

SELECT p.title, u.name AS author
FROM posts p
JOIN users u ON u.id = p.author_id
WHERE p.published = true
ORDER BY p.created_at DESC;

1.3 数据表管理的最佳实践

  • 所有结构变更都通过迁移文件 ,不要手动执行 ALTER TABLE。
  • 迁移文件提交到版本控制,方便回滚和追溯变更历史。
  • 不要修改已执行的迁移,新增迁移来修正。
  • 为外键和频繁查询字段建索引。
  • 生产环境迁移前先备份。

第二部分:Prisma 与 Drizzle 深度对比

2.1 核心架构差异

两者的根本分歧在于类型从哪里来。

维度 Prisma Drizzle
查询引擎 Rust 二进制(Prisma 7 起改为纯 TS) 纯 TypeScript,进程内运行
Schema 格式 .prisma DSL 文件 TypeScript 模块
类型生成 构建期 prisma generate 预计算 编译期 TypeScript 结构推断
运行时依赖 @prisma/client + 引擎 零运行时依赖
SQL 访问 抽象 API + $queryRaw 直接 SQL-like API + sql 模板

Prisma 在 prisma generate 阶段预计算类型并写入 .d.ts 文件,编辑器直接加载这些类型;Drizzle 则让 TypeScript 编译器在每次写查询时从 schema 实时推断类型。

这个差异带来一个关键后果:Prisma 的类型检查性能稳定且可预测,因为大部分计算提前完成了;Drizzle 的类型检查开销随查询复杂度和项目规模增长而增加。在基准测试中,Prisma 的 schema 类型生成只需要 428 次类型实例化,而 Drizzle 需要 5,017 次,差距约 91%。

2.2 Schema 定义方式

Prisma 使用专用的 Schema 语言:

prisma 复制代码
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
  author   User    @relation(fields: [authorId], references: [id])
  authorId Int
}

DSL 写法简洁,但脱离了 TypeScript 的 IDE 感知------重构工具、lint 规则无法直接作用于它。

Drizzle 用 TypeScript 定义 schema:

ts 复制代码
import { pgTable, serial, varchar, text, integer, timestamp } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial().primaryKey(),
  email: varchar({ length: 255 }).unique().notNull(),
  name: varchar({ length: 100 }),
  createdAt: timestamp().defaultNow(),
});

export const posts = pgTable('posts', {
  id: serial().primaryKey(),
  title: varchar({ length: 255 }).notNull(),
  authorId: integer('author_id').references(() => users.id),
});

因为 schema 就是 TypeScript,重构、lint、自动补全天然可用。代价是模型变复杂后,schema 文件会变得冗长。

2.3 查询 API 哲学

Prisma 的查询映射到应用层概念,把 SQL 抽象掉了:

ts 复制代码
const user = await prisma.user.findUnique({
  where: { email: 'alice@example.com' },
  include: { posts: { where: { published: true } } },
});

Drizzle 的查询贴近 SQL,像链式构建器一样组合:

ts 复制代码
const result = await db
  .select()
  .from(users)
  .leftJoin(posts, eq(posts.authorId, users.id))
  .where(eq(users.email, 'alice@example.com'));

前者上手更快,后者对 SQL 熟悉的人更自然。Vercel 的对比文档指出,这个差异直接影响代码审查的方式、新人上手速度,以及出问题时调试的难度。

2.4 迁移工作流

Prisma Migrate 提供了一套完整的声明式迁移流程:

bash 复制代码
npx prisma migrate dev --name add_user_role    # 开发环境
npx prisma migrate deploy                       # 生产环境

Prisma Migrate 内置了数据丢失检测、advisory locking 和确认流程 ,开发迁移时需要影子数据库(shadow database)。生产环境务必使用 migrate deploy,绝不能用 migrate dev。

Drizzle Kit 的工作流更接近传统 SQL 迁移工具:

bash 复制代码
npx drizzle-kit generate    # 从 schema 差异生成 SQL 文件
npx drizzle-kit migrate     # 按顺序应用未执行的迁移

Drizzle Kit 生成的是纯 SQL 文件 ,方便在 PR 中审查。它对数据丢失检测和回滚的自动化程度较低,更多依赖团队自身的流程管理。生产环境同样应该用 generate + migrate,而不是 push。

2.5 性能对比

Prisma 7 将 Rust 查询引擎替换为纯 TypeScript 客户端后,性能差距大幅缩小------包体积缩小约 90%,查询速度提升最高 3 倍。Drizzle 的运行时仍然显著更小。

根据 2026 年的基准数据:

指标 Drizzle Prisma
包体积 ~35KB ~230KB
冷启动 ~10ms ~250ms
简单 SELECT 基准 慢 2-3 倍
复杂 JOIN 基准 慢 2-5 倍
内存占用 ~10MB ~50MB

不过 Prisma 8(基于 TypeScript 的新基础)已经达到原生 pg 驱动约 87% 的峰值吞吐量,比 Prisma 7 高出 52%。在高负载场景下,Prisma 的性能表现正在快速收敛。

在读写特征上,有研究显示 Prisma 在轻到中等负载的读操作中延迟更低,而 Drizzle 在 create、update、delete 操作上表现更好。

2.6 边缘运行时与 Serverless

这是 Drizzle 的传统优势领域。Drizzle 设计上就是 serverless-ready 的,包体积小巧,零依赖,支持 Node.js、Bun、Deno、Cloudflare Workers 以及各类 Edge Runtime。

Prisma 在边缘环境方面也在追赶。Prisma 7 移除 Rust 引擎后,包体积大幅缩小,配合 Driver Adapters 可以在边缘环境运行,但配置复杂度仍然高于 Drizzle。Prisma Accelerate 则提供了连接池和缓存方案来缓解 serverless 场景下的连接耗尽问题。

2.7 工具链与生态

Prisma 的工具链更完整:

  • Prisma Studio:可视化数据浏览器
  • Prisma Accelerate:连接池与缓存
  • Prisma Pulse:实时数据库事件
  • 内置连接池管理
  • Client Extensions 支持中间件

Drizzle 的工具链更精简:

  • Drizzle Studio:数据库浏览器
  • 连接池需要自带(pg Pool、postgres.js 等)
  • 没有内置的中间件系统,需要通过 SQL 钩子实现
  • 生态相对年轻,示例和社区资源不如 Prisma 丰富

在 npm 下载量上,Prisma 仍然领先:截至 2026 年 7 月,Prisma 月下载量 5530 万,Drizzle 4810 万。

2.8 选型决策指南

你的情况 推荐
团队 SQL 经验丰富,想要精细控制 Drizzle
需要边缘运行时或极小包体积 Drizzle
追求最佳开发体验和类型安全 Prisma
想要完整的迁移、Studio、监控工具链 Prisma
项目模型多、类型检查速度敏感 Prisma
无服务器架构,冷启动敏感 Drizzle
需要中间件、扩展、实时事件 Prisma

一句话总结:

Prisma 是"抽象优先,用构建期代码生成换类型安全和开发效率";Drizzle 是"SQL 优先,用 TypeScript 推断换轻量、可控和边缘友好"。

两者不是在竞争"谁的质量更好",而是在竞争"团队愿意接受哪种构建期成本"------一个代码生成步骤,还是更重的类型检查。

结语

数据库层没有银弹。先通过原生 SQL 建立对数据模型和查询的直觉,再根据团队的技术偏好和部署约束选择 ORM,才是成熟的做法。如果你追求开箱即用的完整工具链和最佳 DX,Prisma 是稳妥的选择;如果你想要 SQL 级别的控制力和极致的运行时轻量,Drizzle 值得认真考虑。

相关推荐
倔强的石头_1 小时前
慢接口定位实战:从应用日志一路追到 SQL 执行计划
数据库
考虑考虑1 小时前
SQL中的 CASE WHEN
数据库·后端·sql
SelectDB1 小时前
Doris 替换 ClickHouse:部署核查命令、常见问题排查与适配说明
大数据·数据库·数据分析
Nturmoils1 小时前
OceanBase VS 金仓:同一组复杂 SQL,分布式与集中式架构怎么跑
数据库
旺仔不是程序员1 小时前
索引膨胀与重建:PostgreSQL REINDEX 生产实践
数据库·后端·sql
IT枫斗者枫哥1 小时前
MyBatis只查两次,为什么还会查到别家客户?
java·数据库
SelectDB1 小时前
日志成本打不下来?Doris 降本实操笔记:建表、参数、冷热分层与四个排错现场
大数据·数据库·数据分析
这个DBA有点耶1 小时前
数据库迁移不停机方案:双轨并行技术架构与3TB核心系统落地实践
数据库·架构·dba
SelectDB1 小时前
Doris vs ClickHouse:企业级分析场景下,OLAP 的能力边界正在如何变化?
大数据·数据库·数据分析