编程 Drizzle ORM 深度拆解:当 TypeScript 决定「干掉全部传统 ORM」——一个 35K Star 的查询构建器如何用 JIT 编译和零运行时抽象重新定义数据库访问的终极形态

2026-08-04 18:14:32 +0800 CST views 6

Drizzle ORM 深度拆解:当 TypeScript 决定「干掉全部传统 ORM」——一个 35K Star 的查询构建器如何用 JIT 编译和零运行时抽象重新定义数据库访问的终极形态

引言:ORM 世界的一场静默革命

2026 年的 TypeScript 生态正在经历一场底层范式转移。当 Biome 用 Rust 碾压 ESLint,当 Bun 用 Zig 挑战 Node.js,Drizzle ORM 正在用一种截然不同的方式重新定义「TypeScript 应该如何访问数据库」。

v1.0.0-rc.1 的基准测试震动了整个社区:Bun + Drizzle 的平均延迟 7.3ms,吞吐 8.8k req/sec;Go v1.25.5 + 数据库驱动的平均延迟 18.1ms,吞吐 8.2k req/sec。JavaScript 在数据库访问层跑出了比 Go 更低的延迟——这件事放在三年前几乎是天方夜谭。

这不是一个「更快的 ORM」的故事。这是一个关于「ORM 到底应该是什么」的哲学之争,Drizzle 团队用四年时间给出了一个激进的答案:ORM 不该是一个黑箱,它应该是一个类型安全的 SQL 操作层

本文将从 Drizzle ORM 的架构设计、核心机制、性能优化、生态集成四个维度,深度拆解这个 35K Star 开源项目的工程哲学和实现细节。


一、架构设计:为什么 Drizzle 不是「另一个 ORM」

1.1 传统 ORM 的根本问题

在理解 Drizzle 之前,先看看传统 ORM 到底哪里出了问题。

以 Prisma 为例,它的工作流是这样的:

// Prisma 的工作方式
const user = await prisma.user.findUnique({
  where: { email: 'test@example.com' },
  include: { posts: true }
});

看起来很优雅?但背后发生了什么?

  1. Prisma Client 是一个自动生成的 JavaScript 客户端,体积庞大
  2. 查询语句使用 Prisma 自己的 DSL(领域特定语言),和 SQL 完全不同
  3. 运行时需要一个独立的 Prisma Engine 进程(Rust 编写的二进制)
  4. 每次查询都要经过 DSL → Engine → SQL → 执行 → 结果映射 的完整链路
  5. 结果映射使用通用的动态字段遍历,无法针对具体 Schema 做优化

这意味着什么?你的每一次数据库查询,都在为 Prisma 的抽象层买单

1.2 Drizzle 的反直觉设计哲学

Drizzle 的核心理念可以用一句话概括:TypeScript 代码就是 SQL,SQL 就是 TypeScript 代码

// Drizzle 的工作方式
import { drizzle } from 'drizzle-orm/node-postgres';
import { users, posts } from './schema';

const db = drizzle(process.env.DATABASE_URL!);

// 这就是 SQL,只是用 TypeScript 写的
const result = await db.select().from(users)
  .where(eq(users.email, 'test@example.com'))
  .leftJoin(posts, eq(users.id, posts.authorId));

看到区别了吗?

  1. 没有自动生成的客户端——Schema 就是普通的 TypeScript 文件
  2. 没有 DSL——查询语句就是 SQL 的类型安全版本
  3. 没有独立的 Engine 进程——直接调用底层数据库驱动
  4. 没有动态结果映射——通过 JIT 编译生成专用的映射函数

Drizzle 的架构可以用三层来理解:

┌─────────────────────────────────────────┐
│         Type-safe Query Builder         │  ← 开发者使用的 API 层
├─────────────────────────────────────────┤
│       JIT Row Mapper / SQL Generator    │  ← 核心优化层(v1.0 新增)
├─────────────────────────────────────────┤
│    Database Driver (pg / mysql2 / ...)  │  ← 底层驱动层
└─────────────────────────────────────────┘

这种分层设计带来的直接好处是:Drizzle 的运行时依赖几乎为零。它不会在你的 node_modules 里塞一个几百 MB 的 Engine 二进制,也不会在运行时启动一个独立进程。

1.3 Schema 即代码:TypeScript-first 的定义方式

Drizzle 的 Schema 定义方式是它最直观的区别特征:

// schema.ts
import { pgTable, serial, text, timestamp, integer } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: text('email').notNull().unique(),
  fullName: text('full_name'),
  createdAt: timestamp('created_at').defaultNow(),
});

export const posts = pgTable('posts', {
  id: serial('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content'),
  authorId: integer('author_id').references(() => users.id),
  publishedAt: timestamp('published_at'),
});

这段代码做了几件事:

  1. 定义了表结构(字段名、类型、约束)
  2. 定义了字段映射关系(TypeScript 的 fullName → 数据库的 full_name
  3. 定义了外键关系
  4. 这些信息会被 TypeScript 编译器完全验证

与 Prisma 的 .prisma schema 文件不同,Drizzle 的 schema 是完全可组合的。你可以用 TypeScript 的模块系统组织 schema,可以用泛型抽象通用模式,可以用条件类型做动态表定义。


二、核心机制深度剖析

2.1 类型安全查询引擎:从 API 到 SQL 的全链路推导

Drizzle 最让人印象深刻的能力是它的类型推导。当你写下一个查询,TypeScript 编译器能在编译时就知道结果的精确类型:

// 查询结果的类型是自动推导的
const result = await db.select({
  userId: users.id,
  userName: users.fullName,
  postTitle: posts.title,
}).from(users)
  .leftJoin(posts, eq(users.id, posts.authorId));

// result 的类型精确到每个字段
// result[0].userId → number
// result[0].userName → string | null
// result[0].postTitle → string | null

这背后的技术实现相当精巧。Drizzle 的查询构建器基于 TypeScript 的泛型和条件类型系统,构建了一套完整的「SQL AST(抽象语法树)→ TypeScript 类型」的推导管道:

// 简化的类型推导逻辑
type SelectResult<TSelect extends Record<string, any>> = {
  [K in keyof TSelect]: 
    TSelect[K] extends SQL<infer T> ? T :
    TSelect[K] extends Column<infer T> ? T :
    never;
};

这意味着:

  • 你不能引用不存在的字段(编译时报错)
  • 你不能对 text 字段做数值运算(编译时报错)
  • JOIN 后的结果类型自动合并(包含可空性标记)
  • 聚合函数的结果类型自动推导(countnumbersumnumber | null

2.2 JIT Row Mapper:把 ORM 开销压到接近零

这是 Drizzle v1.0 最核心的技术突破。

问题:传统 ORM 的映射开销

传统 ORM 在处理查询结果时,每条记录都要经过一个通用的映射流程:

// 传统 ORM 的通用映射伪代码
function mapRow(row, schema) {
  const obj = {};
  for (const field of schema.fields) {
    // 动态判断类型、做转换、处理嵌套关系
    obj[field.name] = convertType(row[field.dbName], field.type);
  }
  return obj;
}

这个函数要处理所有可能的字段类型、所有可能的表结构,所以它必须包含大量的动态判断逻辑。在高并发场景下,这些「通用化」的判断会累积成可观的性能损耗。

Drizzle 的解法:编译时专用映射

Drizzle 的 JIT Row Mapper 在运行时针对你的具体 Schema 编译出一个专用的映射函数

// Drizzle 的 JIT 映射(概念性伪代码)
// 编译阶段:分析 Schema,生成专用映射函数
function createRowMapper(schema) {
  // 直接生成最短路径的映射代码
  return function mapRow(row) {
    return {
      id: row[0],           // 直接用索引,不用字段名查找
      email: row[1],
      full_name: row[2],
      created_at: row[3],
    };
  };
}

// 运行阶段:直接调用专用函数,零动态判断
const mapper = createRowMapper(usersSchema);
const result = rows.map(mapper);

这不是简单的「把映射函数缓存起来」。Drizzle 的 JIT 映射是在首次使用某个 Schema 查询时,根据 Schema 的精确信息生成一个专门处理这张表的映射函数。这个函数:

  1. 不包含任何动态字段名查找——直接用数组索引
  2. 不包含任何类型判断——字段类型在编译时已知
  3. 不包含任何通用化抽象——每个 Schema 一个专用函数

官方的说法是把 ORM overhead 压到接近 0,性能和手写 raw driver 代码一个量级。

性能对比:数据说话

让我们看看真实的基准测试数据(Drizzle v1.0.0-rc.1, Bun v1.3.13):

指标Drizzle + Bun SQLGo v1.25.5差异
平均延迟7.3ms18.1msDrizzle 快 60%
吞吐量8.8k req/sec8.2k req/secDrizzle 高 7%
CPU 使用81.6%75.5%Go 低 6%

关键洞察:Drizzle 用更高的 CPU 换来了更低的延迟。这不是「免费的性能提升」,而是用算力换响应速度的工程权衡。对于延迟敏感的应用(API 网关、实时服务),这个权衡是值得的。

需要注意的是,这份 benchmark 是 Drizzle 团队自己跑的,Go 那侧的具体 ORM 和框架配置没有完全公开。但即便打个折扣,Drizzle + Bun 的表现也已经足够亮眼。

2.3 SQL 生成器:透明且可预测

Drizzle 的 SQL 生成器是它「反 ORM」哲学的另一个体现。每个查询构建器方法最终都会生成标准 SQL,而且你可以随时查看生成的 SQL:

// 生成 SQL 并打印
const query = db.select().from(users)
  .where(eq(users.email, 'test@example.com'))
  .orderBy(desc(users.createdAt))
  .limit(10);

console.log(query.toSQL());
// {
//   sql: 'SELECT * FROM "users" WHERE "email" = $1 ORDER BY "created_at" DESC LIMIT $10',
//   params: ['test@example.com']
// }

这种透明性带来了几个好处:

  1. 调试简单——看到 SQL 就知道发生了什么
  2. 性能优化直接——可以检查索引是否命中
  3. 学习曲线平滑——懂 SQL 就能用 Drizzle
  4. 迁移友好——从 raw SQL 迁移到 Drizzle 几乎无成本

三、生态集成与实战

3.1 数据库支持矩阵

Drizzle 的一大优势是它对多种数据库的原生支持:

数据库驱动支持Edge 支持特殊功能
PostgreSQLnode-postgres, Neon, Supabase, Vercel PostgresRLS, Views, Materialized Views
MySQLmysql2USE INDEX, FORCE INDEX
SQLitebetter-sqlite3, Bun SQL, libSQL/TursoWAL mode, FTS5
SingleStore单Store 驱动向量搜索
GelGel 客户端EdgeDB 兼容

特别值得一提的是对边缘计算的支持。Drizzle 的包体积很小(无运行时依赖),天然适合部署到 Cloudflare Workers、Vercel Edge Functions 等边缘环境:

// Cloudflare Workers + Drizzle
import { drizzle } from 'drizzle-orm/d1';
import { users } from './schema';

export default {
  async fetch(request, env) {
    const db = drizzle(env.DB);
    const allUsers = await db.select().from(users);
    return new Response(JSON.stringify(allUsers));
  }
};

3.2 Drizzle Kit:数据库迁移工具

Drizzle Kit 是官方的数据库迁移工具,支持:

  • Schema 推断introspect)——从现有数据库生成 TypeScript Schema
  • 迁移生成generate)——从 Schema 变更生成 SQL 迁移文件
  • 直接推送push)——将 Schema 变更直接同步到数据库
  • 迁移执行migrate)——执行待处理的迁移文件
# 从数据库推断 Schema
npx drizzle-kit introspect

# 生成迁移文件
npx drizzle-kit generate

# 直接推送到数据库(开发环境)
npx drizzle-kit push

Drizzle Kit 的一个重要设计决策是迁移文件是纯 SQL。这意味着你可以轻松地将 Drizzle 的迁移集成到任何现有的数据库管理流程中,不需要特殊的工具链。

3.3 Drizzle Studio:可视化数据库管理

Drizzle Studio 是一个基于 Web 的数据库管理工具,可以直接连接到你的数据库进行数据浏览和编辑:

# 启动 Drizzle Studio
npx drizzle-studio

它支持:

  • 浏览所有表和视图
  • 编辑数据(增删改查)
  • 查看 Schema 结构
  • 执行自定义 SQL 查询

与 Prisma Studio 相比,Drizzle Studio 更轻量,启动更快,而且是开源的。

3.4 验证集成:drizzle-zod / drizzle-valibot / drizzle-typebox

Drizzle 提供了与主流验证库的深度集成,可以直接从 Schema 生成验证 Schema:

import { createInsertSchema } from 'drizzle-zod';
import { users } from './schema';

// 从 Drizzle Schema 自动生成 Zod 验证 Schema
const insertUserSchema = createInsertSchema(users, {
  email: (schema) => schema.email.email(),
  fullName: (schema) => schema.fullName.min(2).max(100),
});

// 验证请求数据
const userData = insertUserSchema.parse(req.body);

这种集成消除了「Schema 定义一次,验证规则再定义一次」的重复劳动。

3.5 Effect 集成:函数式编程的数据库访问

v1.0 新增了对 Effect v4 的原生支持,可以在 Effect 的类型安全错误处理框架中使用 Drizzle:

import { PgClient } from '@effect/sql-pg';
import * as PgDrizzle from 'drizzle-orm/effect-postgres';
import * as Effect from 'effect/Effect';

const DB = PgDrizzle.make({ relations }).pipe(
  Effect.provide(PgDrizzle.DefaultServices)
);

const program = Effect.gen(function*() {
  const db = yield* DB;
  const users = yield* db.select().from(usersTable);
  // 错误类型全程有推导,不需要手动 try/catch
}).pipe(Effect.provide(PgClientLive));

await Effect.runPromise(program);

如果你的项目已经在用 Effect,这个集成补上了数据库访问这一块的关键缺口。


四、Drizzle vs Prisma:不是简单的替代关系

社区里经常有人问「Drizzle 能替代 Prisma 吗?」这个问题其实问错了。两者的设计哲学和适用场景有本质区别:

4.1 核心差异对比

维度DrizzlePrisma
设计理念SQL-first,透明可预测DSL-first,抽象封装
运行时依赖几乎为零需要 Prisma Engine
学习曲线懂 SQL 就能上手需要学 Prisma DSL
包体积~100KB~5MB+(含 Engine)
Edge 支持原生支持受限
类型安全编译时全链路推导编译时 + 运行时验证
迁移工具Drizzle Kit(纯 SQL)Prisma Migrate
文档质量良好,持续改进优秀,行业标杆
社区生态快速增长中成熟庞大
关联查询支持,但需手动 JOIN自动生成 JOIN

4.2 什么时候选 Drizzle

  • 新项目,尤其是 Edge-first 项目——Drizzle 的轻量级特性完美契合
  • Bun / Hono / Elysia 技术栈——Drizzle 在这些框架的社区里已经成了默认选择
  • 团队熟悉 SQL——学习曲线几乎为零
  • 性能敏感场景——JIT 映射的性能优势是实打实的
  • TypeScript 重度用户——类型推导的体验确实更好

4.3 什么时候选 Prisma

  • 团队不熟悉 SQL——Prisma 的 DSL 对新手更友好
  • 需要复杂的关联查询——Prisma 的 includeselect 语法更直观
  • 需要成熟的数据库迁移方案——Prisma Migrate 的功能更完整
  • 已有大型 Prisma 项目——迁移成本可能不值得
  • 需要完善的文档和社区支持——Prisma 的文档是行业标杆

4.4 迁移指南

如果你决定从 Prisma 迁移到 Drizzle,以下是关键步骤:

// Prisma 代码
const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true }
});

// 等价的 Drizzle 代码
const result = await db.select()
  .from(users)
  .leftJoin(posts, eq(users.id, posts.authorId))
  .where(eq(users.id, 1));

主要的思维转换:

  1. 从 DSL 思维转为 SQL 思维——Drizzle 的查询就是 SQL
  2. 从隐式 JOIN 转为显式 JOIN——需要手动指定 JOIN 条件
  3. 从自动生成转为手动定义——Schema 和关系需要手动维护

五、v1.0 的其他重要变化

5.1 Casing API 重构(Breaking Change)

v1.0 统一了字段命名风格的处理方式:

// 新的 casing API
import * as d from "drizzle-orm/pg-core";

// snake_case 策略:fullName 在数据库里存为 full_name
const users = d.snakeCase.table("users", {
  id: d.serial().primaryKey(),
  email: d.text().unique(),
  fullName: d.text(),
  createdAt: d.timestamp().defaultNow(),
});

// 整个 schema 统一用 camelCase
const schema2 = d.camelCase.schema("schema2");
const usersInSchema2 = schema2.table("users", ...);

这个变化让命名策略的配置更清晰了,但如果你的现有项目用了旧的配置方式,升级前务必仔细阅读迁移说明。

5.2 Drizzle for LLM Agents(Preview)

Drizzle 正在开发一个面向 AI Agent 的接口,让 LLM 可以直接理解和操作数据库结构。这个功能还在预览阶段,但代表了 ORM 未来的一个重要方向——让 AI 成为数据库访问的一等公民

5.3 多方言支持扩展

v1.0 新增了对 Gel(EdgeDB 兼容)和 SingleStore 的支持,进一步扩大了 Drizzle 的数据库覆盖面。


六、性能优化实战

6.1 连接池配置

Drizzle 本身不管理连接池,而是依赖底层驱动的连接池配置。对于 Node.js + PostgreSQL 场景:

import { Pool } from 'pg';
import { drizzle } from 'drizzle-orm/node-postgres';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,           // 最大连接数
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

const db = drizzle(pool);

6.2 Prepared Statements

对于高频查询,使用 Prepared Statement 可以显著提升性能:

// 预编译语句
const findUserByEmail = db.select().from(users)
  .where(eq(users.email, placeholder('email')))
  .prepare('findUserByEmail');

// 多次执行同一查询,只编译一次 SQL
const user1 = await findUserByEmail.execute({ email: 'test@example.com' });
const user2 = await findUserByEmail.execute({ email: 'another@example.com' });

6.3 批量操作优化

// 批量插入
await db.insert(users).values([
  { email: 'user1@example.com', fullName: 'User 1' },
  { email: 'user2@example.com', fullName: 'User 2' },
  // ... 更多记录
]);

// 批量更新
await db.update(users)
  .set({ fullName: 'Updated' })
  .where(inArray(users.id, [1, 2, 3]));

6.4 索引优化建议

// 创建索引
import { index } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  // ... 字段定义
}, (table) => [
  index('users_email_idx').on(table.email),
  index('users_created_at_idx').on(table.createdAt),
]);

七、Drizzle 的局限性与注意事项

7.1 当前的不足

  1. 关联查询不如 Prisma 直观——复杂的一对多、多对多关系需要手动写 JOIN
  2. 文档质量在改善但仍有差距——部分高级用法的文档不够完善
  3. 迁移工具功能有限——对于复杂的数据迁移场景,不如 Prisma Migrate 成熟
  4. rc 版本有 Breaking Change——不建议在生产环境使用 rc 版本

7.2 升级注意事项

如果你从旧版本升级到 v1.0:

  1. Casing API 变更——需要更新所有 table 定义
  2. 部分 API 重命名——检查 changelog 中的 breaking changes
  3. 测试覆盖——确保所有查询在升级后仍然正确

八、总结与展望

Drizzle ORM 用四年时间证明了一件事:TypeScript ORM 可以既轻量又高性能。它的成功不是因为做了什么「黑科技」,而是因为做了一个正确的设计决策——不发明新的抽象,而是把 SQL 用类型安全的方式暴露出来

v1.0 的发布标志着 Drizzle 从「有趣的实验」变成了「生产级的选择」。JIT Row Mapper 的性能突破、Effect v4 的原生集成、持续扩大的数据库支持矩阵,都在巩固它的技术护城河。

展望未来,Drizzle 的几个发展方向值得关注:

  1. LLM Agent 集成——让 AI 直接操作数据库
  2. 更多数据库方言——覆盖更多使用场景
  3. 关联查询优化——提升复杂查询的开发体验
  4. Drizzle Gateway——云端数据库管理平台

对于开发者来说,Drizzle 带来的最大启示可能是:好的抽象不是让你忘记底层,而是让你更好地掌控底层。在这个 AI 编程日益普及的时代,能够透明、可预测地控制数据库访问,反而成了更大的优势。


参考资源:

  • Drizzle ORM 官方文档:https://orm.drizzle.team
  • GitHub 仓库:https://github.com/drizzle-team/drizzle-orm
  • Drizzle Benchmarks:https://orm.drizzle.team/benchmarks
  • Drizzle Studio:https://orm.drizzle.team/drizzle-studio/overview

推荐文章

一个有趣的进度条
2024-11-19 09:56:04 +0800 CST
10个几乎无人使用的罕见HTML标签
2024-11-18 21:44:46 +0800 CST
Rust 高性能 XML 读写库
2024-11-19 07:50:32 +0800 CST
20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
Go 接口:从入门到精通
2024-11-18 07:10:00 +0800 CST
程序员茄子在线接单