Drizzle ORM v1 深度拆解:当 TypeScript 决定「用 JIT 干掉全部 ORM 开销」——从类型安全查询到接近零开销的行映射,一个让 Bun 跑赢 Go 的 ORM 如何重新定义数据库访问的终极形态
引言:2026 年 ORM 赛道的分水岭
2026 年 5 月,Drizzle ORM 发布了 v1.0.0-rc.1,随版本附带的一张基准测试截图在整个开发者社区炸开了锅:Bun + Drizzle 的组合在标准 HTTP 服务端场景下,平均延迟 7.3ms,吞吐 8.8k req/sec;而 Go v1.25.5 的同场景延迟 18.1ms,吞吐 8.2k req/sec。
Bun 官方账号转发时只写了一句话:"faster than Go, uses Bun, Many such cases。"
Vue.js 作者尤雨溪也转发了这条推文,说"正好赶在 Void 公测前把依赖版本更新一下"。
都 2026 年了,JavaScript 服务端的数据库访问层居然比 Go 还快,这事值得好好拆一下。不是为了制造语言对立,而是因为 Drizzle 做对了几件事,这些事情对每一个用 TypeScript 写后端的程序员都有参考价值。
一、Drizzle ORM 到底是什么
1.1 不走寻常路的定位
Drizzle ORM 是 2022 年前后开始进入视野的 TypeScript 原生数据库操作库。它的定位和传统 ORM 有本质区别——它更接近一个类型安全的 SQL 查询构建器,而不是一个"把数据库表映射成对象"的传统 ORM。
用过 Prisma 的人都知道,Prisma 走的是"Schema DSL 自动生成一切"的路子。你写一个 schema.prisma 文件,Prisma 帮你生成客户端、迁移文件、类型定义。这在项目初期很爽,但随着项目变复杂,你会发现自己越来越难控制最终执行的 SQL。
Drizzle 的做法完全不同:
import * as d from "drizzle-orm/pg-core";
// 直接用 TypeScript 定义 Schema
export const users = d.pgTable("users", {
id: d.serial().primaryKey(),
email: d.text().unique().notNull(),
name: d.text(),
createdAt: d.timestamp().defaultNow().notNull(),
});
// 查询写起来就像在写 SQL
const result = await db
.select()
.from(users)
.where(d.eq(users.email, "test@example.com"));
这种方式有几个好处:
- 你写的 TypeScript 就是 Schema,不需要学额外的 DSL
- 查询语句是代码风格,IDE 补全和类型推导都很自然
- 最终执行的 SQL 一目了然,不会出现 Prisma 那种"看着简单但生成了一堆 JOIN"的情况
- 体积很小,没有复杂的运行时依赖,部署到 Cloudflare Workers、Vercel Edge 等 Edge 环境毫无压力
1.2 生态位:填补 TypeScript 全栈的数据库层空白
在 Drizzle 出现之前,TypeScript 全栈开发者的数据库选择要么是 Prisma(功能全但重),要么是 TypeORM(老派且类型支持弱),要么是直接用 pg / mysql2 这类底层驱动(类型安全得自己写)。
Drizzle 填补了中间这个空白:足够轻量可以在 Edge 环境运行,足够类型安全可以享受 TypeScript 的全部好处,足够接近 SQL 可以让有经验的开发者精确控制查询行为。
这两年在 Hono、Elysia、Fresh 这类轻量 Web 框架圈子里,Drizzle 的采用率一路走高。不是因为它"新",而是因为它"对"。
二、v1.0.0-rc.1 的四大核心变化
2.1 JIT Row Mappers:把 ORM 开销压到接近零
这是这次版本最核心的特性,也是那张基准测试截图的数据来源。
传统 ORM 的性能瓶颈在哪
先说 ORM 的开销从哪来。传统 ORM 每次查询完,都要把数据库返回的原始行(通常是数组格式)动态地映射成 JavaScript 对象。这个过程包括:
- 遍历每一行的每个字段
- 根据 Schema 定义做类型转换(字符串 → Date、字符串 → JSON 等)
- 处理关联关系(如果有的话)
- 构造完整的 JavaScript 对象
在高并发场景下,这个过程会被放大。假设你每秒处理 1000 个请求,每个请求返回 50 行数据,每行 20 个字段——那就是每秒 100 万次字段映射操作。通用 ORM 框架为了兼容各种 Schema,不得不做大量的动态判断和条件分支,这些"通用化"的代价就是性能损耗。
Drizzle 的 JIT 解法
Drizzle 的解法是在运行时针对你的具体 Schema 编译出一个专用的映射函数。
// 传统 ORM 的通用映射(伪代码)
function通用映射(row, schema) {
const obj = {};
for (const field of schema.fields) {
switch (field.type) {
case 'string':
obj[field.name] = String(row[field.column]);
break;
case 'number':
obj[field.name] = Number(row[field.column]);
break;
case 'date':
obj[field.name] = new Date(row[field.column]);
break;
// ... 更多类型判断
}
}
return obj;
}
// Drizzle JIT 编译出的专用映射(伪代码)
function usersTable映射(row) {
return {
id: row[0], // 直接取,无类型判断
email: row[1], // 直接取,无类型判断
name: row[2] === null ? null : row[2],
createdAt: new Date(row[3]),
};
}
关键区别在于:
- 传统 ORM:每次映射都要判断字段类型、处理各种边界情况
- Drizzle JIT:针对你的 Schema 生成专用函数,只处理你这张表的字段,走最短路径
你可以理解成 JIT 编译器给你的 Schema 量身定制了一条快速通道,省掉了所有通用 ORM 框架里不必要的抽象层。官方的说法是把 ORM overhead 压到接近 0,性能和手写 raw driver 代码一个量级。
这意味着什么
对于大多数 Web 应用来说,数据库访问层的 ORM 开销通常占总请求时间的 5%-15%。JIT row mapper 把这部分压缩到接近零,相当于给你的应用免费加了一层缓存。
更重要的是,这个优化对开发者是完全透明的。你不需要改变任何查询写法,只需要升级 Drizzle 版本,就能自动享受到 JIT 带来的性能提升。
2.2 Effect v4 原生支持
Effect 是 TypeScript 生态里的一个函数式编程库,提供类型安全的错误处理、依赖注入和并发管理。上个月 Effect v4 发布,Drizzle 同步跟进,可以直接在 Effect 的 gen 函数里用 yield 操作数据库。
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 的人来说这个特性可以先跳过,但如果你的项目已经在用 Effect,这次集成补上了一块重要的缺口。Effect 的类型安全错误处理和 Drizzle 的类型安全查询结合在一起,可以实现从数据库查询到业务逻辑的全链路类型推导。
2.3 Casing API 重构(Breaking Change)
之前 Drizzle 处理字段命名风格转换(camelCase 和 snake_case 互转)的方式比较分散,这次统一了。
新的写法是在创建 table 时直接声明命名策略:
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", {
// ...
});
逻辑更清晰了,但如果你的现有项目用了旧的 casing 配置方式,升级前务必仔细读一遍官方迁移说明。这是一个 breaking change,不兼容旧的写法。
2.4 Drizzle for LLM Agents(Preview)
这个特性还在预览阶段,目标是让 LLM Agent 可以直接理解和操作 Drizzle 管理的数据库结构。
这个方向很有意思。想象一下,你的 AI 编程助手可以直接读取 Drizzle 的 Schema 定义,理解表结构、字段类型、关联关系,然后自动生成符合你项目风格的查询代码。这比让 AI 直接写 SQL 要安全得多,因为 Drizzle 的类型系统可以作为一道防线。
具体实现在 drizzle-orm@ai 分支上,感兴趣可以去他们的 Discord 联系。
三、那张基准测试,到底该怎么看
3.1 测试环境
先说结论:数字是真实的,但不能直接理解成"JavaScript 天生比 Go 快"。
Drizzle 测的是一个完整的 HTTP 服务端场景:
| 项目 | Drizzle 侧 | Go 侧 |
|---|---|---|
| 运行时 | Bun v1.3.13 | Go v1.25.5 |
| 数据库驱动 | Bun SQL | 未明确 |
| ORM | Drizzle v1.0.0-rc.1 (JIT) | 未明确 |
| 平均延迟 | 7.3ms | 18.1ms |
| 吞吐 | 8.8k req/sec | 8.2k req/sec |
| CPU 负载 | 81.6% | 75.5% |
3.2 关键细节
延迟差距明显,但吞吐差距不大。 7.3ms vs 18.1ms 的延迟差距超过 2 倍,但 8.8k vs 8.2k 的吞吐差距只有 7%。这说明 Drizzle + Bun 在单个请求的处理速度上有优势,但在并发处理能力上和 Go 差距不大。
CPU 负载是关键。 Drizzle 侧 81.6%,Go 侧 75.5%——Drizzle 多消耗了大约 6 个百分点的 CPU 换来更低的延迟。换句话说,这不是免费的,是用算力换响应速度。
Go 那侧的配置不透明。 推文里没有明确列出 Go 那侧用的是哪个 ORM 和框架配置。如果 Go 那侧用的是 GORM 而不是更高效的 sqlx,或者框架配置没有针对高并发做优化,那这个对比就不够公平。
3.3 正确的解读方式
这份 benchmark 是 Drizzle 自己跑的,拿来当参考可以,直接下"JS 比 Go 快"的结论还是谨慎一点。
但不管怎么说,Drizzle + Bun 能跑出这个水平,放在两三年前几乎是不可能的事。Bun 把 JavaScript 运行时的性能天花板往上推了一大截,Drizzle 的 JIT 映射又消掉了 ORM 层的额外损耗,两者叠加才有了这次的结果。
对于正在选型的团队来说,这个 benchmark 的真正意义是:TypeScript 全栈方案在数据库访问层的性能已经不再是短板。以前选 Go 做后端的一个重要理由是"性能好",现在这个理由的说服力在减弱。
四、Drizzle vs Prisma:2026 年的正确选择
Drizzle 在推文里顺带提了一句 Prisma:"4 years in, Drizzle is still just doing Drizzle。8 years in, Prisma is doing whatever they can to stay?"
这话有点扎心,但反映了社区的真实情绪。
4.1 Prisma 的困境
Prisma 最近处境确实有些尴尬。v7 做了一堆更新,但社区对它的路线出现了分歧:
- 越来越重:Prisma 的"自动生成一切"哲学导致它的运行时体积不断膨胀,部署到 Edge 环境越来越困难
- 迁移成本增加:每次大版本升级都有不少 breaking change,社区迁移疲劳
- 性能天花板:Prisma 的动态映射机制决定了它在高并发场景下很难做到极致性能
- SQL 控制力弱:当你需要精确控制生成的 SQL 时,Prisma 的 DSL 会让你抓狂
4.2 Drizzle 的优势
Drizzle 轻量、类型安全、接近 SQL 这几个特点,正好踩在了 Prisma 开始失分的地方:
| 维度 | Drizzle | Prisma |
|---|---|---|
| 体积 | ~50KB | ~5MB+ |
| Edge 支持 | 完美 | 困难 |
| SQL 控制力 | 接近 raw SQL | 受 DSL 限制 |
| 类型推导 | 编译时完全推导 | 部分推导 |
| 学习曲线 | 低(会 SQL 就行) | 中(需学 DSL) |
| 性能 | JIT 接近零开销 | 通用映射有开销 |
| 生态成熟度 | 中等 | 高 |
| 文档质量 | 中等 | 优秀 |
4.3 什么时候选哪个
选 Drizzle 的场景:
- 新项目,特别是用 Bun/Hono/Elysia 等轻量栈的
- 需要部署到 Edge 环境(Cloudflare Workers、Vercel Edge)
- 团队有 SQL 经验,希望精确控制查询
- 对性能有极致要求
选 Prisma 的场景:
- 已有项目在用 Prisma,且运行良好
- 需要复杂的数据库迁移工具
- 团队对 SQL 不熟悉,需要更抽象的接口
- 需要更完善的文档和社区支持
五、实战:用 Drizzle v1 搭建一个生产级 API
5.1 项目初始化
# 创建项目
mkdir drizzle-v1-demo && cd drizzle-v1-demo
bun init
# 安装依赖
bun add drizzle-orm@rc @electric-sql/pglite
bun add -D drizzle-kit@rc @types/bun
# 安装 Hono(轻量 Web 框架)
bun add hono
5.2 定义 Schema
// src/db/schema.ts
import * as d from "drizzle-orm/pg-core";
// 使用 snake_case 策略
export const users = d.snakeCase.table("users", {
id: d.serial().primaryKey(),
email: d.text().unique().notNull(),
name: d.text().notNull(),
passwordHash: d.text("password_hash").notNull(),
role: d.text().default("user").notNull(),
createdAt: d.timestamp("created_at").defaultNow().notNull(),
updatedAt: d.timestamp("updated_at").defaultNow().notNull(),
});
export const posts = d.snakeCase.table("posts", {
id: d.serial().primaryKey(),
title: d.text().notNull(),
content: d.text(),
authorId: d.integer("author_id").references(() => users.id),
published: d.boolean().default(false).notNull(),
createdAt: d.timestamp("created_at").defaultNow().notNull(),
});
5.3 配置数据库连接
// src/db/index.ts
import { drizzle } from "drizzle-orm/pg-core";
import { PgClient } from "@effect/sql-pg";
import * as schema from "./schema";
// 开发环境用 PGlite(无需外部数据库)
// 生产环境换成真正的 PostgreSQL 连接
const db = drizzle({ schema });
export { db };
5.4 编写 API 路由
// src/index.ts
import { Hono } from "hono";
import { db } from "./db";
import { users, posts } from "./db/schema";
import { eq, desc } from "drizzle-orm";
const app = new Hono();
// 获取用户列表
app.get("/api/users", async (c) => {
const allUsers = await db
.select({
id: users.id,
email: users.email,
name: users.name,
role: users.role,
})
.from(users)
.orderBy(desc(users.createdAt));
return c.json(allUsers);
});
// 创建用户
app.post("/api/users", async (c) => {
const body = await c.req.json();
const newUser = await db
.insert(users)
.values({
email: body.email,
name: body.name,
passwordHash: await Bun.password.hash(body.password),
})
.returning({
id: users.id,
email: users.email,
name: users.name,
});
return c.json(newUser[0], 201);
});
// 获取用户的文章
app.get("/api/users/:id/posts", async (c) => {
const userId = parseInt(c.req.param("id"));
const userPosts = await db
.select({
id: posts.id,
title: posts.title,
content: posts.content,
published: posts.published,
})
.from(posts)
.where(eq(posts.authorId, userId))
.orderBy(desc(posts.createdAt));
return c.json(userPosts);
});
export default {
port: 3000,
fetch: app.fetch,
};
5.5 性能调优
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
// v1 新增:启用 JIT 映射
jit: true,
// v1 新增:查询缓存
cache: {
enabled: true,
ttl: 60_000, // 1 分钟
},
});
六、从 Drizzle 看 TypeScript 后端的未来
6.1 JIT 是个信号
Drizzle 的 JIT row mapper 不只是一个性能优化,它代表了一个趋势:TypeScript 生态正在从"够用"走向"极致"。
以前大家对 TypeScript 后端的期望是"能跑就行",性能比不上 Go/Rust 是可以接受的。但 Drizzle 证明了,通过 JIT 编译、运行时优化等手段,TypeScript 在特定场景下可以做到和系统级语言一个量级的性能。
这不是终点。随着 Bun、Deno 等新一代运行时的成熟,TypeScript 后端的性能天花板还会继续被推高。
6.2 Edge 原生是必选项
Drizzle 的轻量设计让它天然适合 Edge 环境。随着 Cloudflare Workers、Vercel Edge Functions、Deno Deploy 等平台的普及,Edge 原生不再是可选项,而是必选项。
那些还在用重量级 ORM 的项目,在迁移到 Edge 环境时会越来越痛苦。Drizzle 这种"从第一天就为 Edge 设计"的思路,是对的方向。
6.3 类型安全不是加分项,是基本要求
Drizzle 能做到"查询写起来像 SQL,但类型推导是完整的",说明 TypeScript 的类型系统已经足够强大,可以在编译时捕获大部分数据库访问错误。
在 2026 年,一个不提供完整类型推导的数据库访问库,已经不应该被考虑了。这不是"有比没有好"的问题,而是"没有就不合格"的问题。
七、迁移指南:从 Prisma 到 Drizzle
如果你决定从 Prisma 迁移到 Drizzle,以下是关键步骤:
7.1 Schema 迁移
// Prisma schema.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String
createdAt DateTime @default(now())
}
// Drizzle schema.ts
import * as d from "drizzle-orm/pg-core";
export const users = d.pgTable("User", {
id: d.serial().primaryKey(),
email: d.text().unique().notNull(),
name: d.text().notNull(),
createdAt: d.timestamp().defaultNow().notNull(),
});
7.2 查询迁移
// Prisma
const users = await prisma.user.findMany({
where: { email: "test@example.com" },
select: { id: true, name: true },
});
// Drizzle
const users = await db
.select({ id: users.id, name: users.name })
.from(users)
.where(eq(users.email, "test@example.com"));
7.3 注意事项
- casing 要统一:v1 的 casing API 是 breaking change,迁移时要仔细处理
- 关系查询要升级:如果你用了 Prisma 的关系查询,需要迁移到 Drizzle v2 的关系查询 API
- 迁移文件要重建:Drizzle 的迁移文件格式和 Prisma 不兼容,需要重新生成
- 逐步迁移:建议一个模块一个模块地迁移,不要一次性全部替换
八、总结与展望
Drizzle ORM v1 的发布,标志着 TypeScript 数据库访问层进入了一个新阶段:
- 性能不再是短板:JIT row mapper 把 ORM 开销压到接近零,TypeScript 后端在数据库访问层的性能已经可以和 Go 媲美
- Edge 原生成为标配:轻量设计让 Drizzle 天然适合 Edge 环境,这是未来的部署方向
- 类型安全达到新高度:从 Schema 定义到查询执行,全链路类型推导已经成为可能
- 生态正在成熟:Effect 集成、LLM Agent 支持等特性,说明 Drizzle 不只是一个查询构建器,而是在构建一个完整的数据库开发生态
对于正在选型的团队,我的建议是:
- 新项目:认真考虑 Drizzle,特别是用 Bun/Hono/Elysia 轻量栈的项目
- 已有 Prisma 项目:不用急着迁移,等 Drizzle v1 正式版稳定后再评估
- Edge 项目:Drizzle 是目前最好的选择,没有之一
TypeScript 后端的黄金时代,可能才刚刚开始。Drizzle 用四年时间证明了一件事:轻量和不慢可以同时做到。这次连 Go 的性能对比都敢直接拿出来,底气已经不一样了。
Drizzle ORM v1.0.0-rc.1 已发布,正式版预计 2026 年 Q3 推出。感兴趣的开发者可以先在新项目中试用,或者去官方 benchmark 页面看完整的测试配置。