万字深度解析:Drizzle ORM+JIT 编译器让 Bun 跑出比 Go 更低延迟——2026 年 TypeScript 后端格局大变
2026年6月,一个来自 Drizzle ORM 团队的基准测试截图,在全球技术社区引发了一场不大不小的震动。
Drizzle v1.0.0-rc.1 发布,官方随手在社交媒体贴了一张 benchmark 截图:左边 Drizzle 跑在 Bun 运行时上,平均延迟 7.3ms,吞吐 8.8k req/sec;右边 Go v1.25.5 标准库实现,平均延迟 18.1ms,吞吐 8.2k req/sec。
JavaScript/TypeScript 生态中的 ORM,在服务端真实场景里,跑出了比 Go 更低的延迟。这个数字让整个社区不得不停下来重新审视一件事:JavaScript 运行时生态的性能天花板,到底被推到了多高?
Bun 官方转发只配了一句话:"faster than Go, uses Bun, Many such cases." 尤雨溪(Vue.js 和 Vite 作者)也转了,顺手说了一句:"正好赶在 Void 公测前把依赖版本更新一下。"
这不是一篇 PR 文案能写出来的热度。这是实打实的基准测试,加上全球顶级开发者社区的自然反应。
今天这篇万字长文,就来把这件事彻底拆清楚:Drizzle ORM 的 JIT 行映射编译器是什么原理?Bun 的 SQL 层为什么能比 Go 更快?TypeScript 全栈为什么在 2026 年变得前所未有地值得认真对待?这背后反映了整个后端开发生态的哪些结构性变化?
一、背景:一个四年的 ORM,是怎么突然出圈的
要理解这次 benchmark 的意义,先得理解 Drizzle ORM 是什么,以及它为什么花了四年时间才走到这一步。
Drizzle ORM 是一个 TypeScript 原生的数据库操作库,2022 年前后正式进入开发者视野。它的诞生背景很清晰:Prisma 太好用,但太重了;raw SQL 太灵活,但类型安全全靠手写。
Drizzle 试图在两者之间找到一条中间路线:Schema 用纯 TypeScript 代码声明,查询用链式 API 构建,最终生成 SQL 语句执行。整个过程从 Schema 定义到查询结果,类型全程推导,编译器参与兜底。
// Drizzle Schema 定义
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: text('email').unique(),
fullName: text('full_name'),
createdAt: timestamp('created_at').defaultNow(),
});
// Type-safe 查询
const result = await db.select().from(users).where(eq(users.email, 'test@example.com'));
// result 的类型完全由 Schema 推导,无需手动标注
和 Prisma 相比,Drizzle 有几个本质区别:
第一,Schema 即代码,不是 DSL。 Prisma 有自己的 Schema 语言(.prisma 文件),有专门的 Prisma CLI 和 Prisma Client 生成机制。Drizzle 的 Schema 就是 TypeScript 代码,直接在你的项目里,不需要任何代码生成步骤,部署时也不需要额外的二进制依赖。
第二,查询即 SQL,不是抽象层。 Prisma 的 Query Engine 在运行时做大量抽象,Drizzle 则尽量让开发者知道最终执行的 SQL 是什么。Drizzle 的查询 API 本质上是一个类型化的 SQL 构建器,最终输出的是标准 SQL 字符串(或参数化查询)。
第三,体积极小,无复杂运行时。 Prisma 的 Query Engine 是一个 Node.js 模块加上 WASM 运行时。Drizzle 没有任何 WASM,没有复杂的 CLI 工具链,发布时就是一个 TypeScript 库文件。
这两年的趋势是:随着 Bun、Deno、Cloudflare Workers 等新型 JavaScript 运行时的崛起,Drizzle 的"轻量 + 无特殊依赖"特性变得越来越有价值。特别是在 Hono、Elysia 这类轻量 Web 框架的生态里,Drizzle 已经成为事实上的数据库操作标准配置。
二、那张 benchmark 图,到底测的是什么
先别急着下"JavaScript 比 Go 快"的结论。我们来仔细拆解这次 benchmark 的测试场景和测试方法。
2.1 测试配置
Drizzle 团队公布的测试配置:
- Bun 运行时:v1.3.13(最新稳定版)
- HTTP 服务:Bun 原生 HTTP 服务器(Bun.serve)
- 数据库驱动:Bun SQL(Bun 内置的数据库操作 API)
- ORM 层:Drizzle ORM v1.0.0-rc.1(含 JIT row mappers)
- 测试目标:PostgreSQL 数据库的 CRUD 操作端点
Go 对比侧的配置没有完全透明公布,但可以确认:
- Go 版本:v1.25.5
- HTTP 框架:Go 标准库 net/http(纯原生实现,无第三方框架加成)
- 数据库驱动:标准 Go database/sql + pgx
这意味着测试对比的是:经过 Drizzle JIT 优化后的 Bun 全栈 vs. Go 标准库原生实现。Go 那侧用的是 Go 社区公认的"保守但稳定的基准配置",而不是某些 benchmark 里故意选慢对比工具的做法。
2.2 核心数据解读
关键数据:
| 指标 | Drizzle + Bun | Go v1.25.5 标准库 |
|---|---|---|
| 平均延迟 | 7.3ms | 18.1ms |
| 吞吐量 | 8.8k req/sec | 8.2k req/sec |
| CPU 占用 | 81.6% | 75.5% |
解读三个要点:
第一,延迟差距是真实的。 7.3ms vs 18.1ms,Go 的延迟高了约 148%。在需要快速响应的场景(比如 API 网关、实时聊天后端、小文件服务等),这个差距是用户体验层面的差异,不是"跑分而已"。
第二,吞吐量差距不大。 8.8k vs 8.2k,差距约 7%,属于统计误差范围内。Go 在吞吐量和 CPU 效率上并不输,Go 侧还有进一步优化的空间。
第三,Drizzle+Bun 用更多的 CPU 换更低的延迟。 CPU 占用 81.6% vs 75.5%,差 6 个百分点。这不是免费的午餐——这是用算力换响应速度。对于追求低延迟的场景,这个 trade-off 是值得的;对于追求吞吐量最大化的场景,可能 Go 更合适。
2.3 不能直接下什么结论
有几个常见的误读需要澄清:
误读一:"JavaScript 比 Go 快。" 不对。这次测试的是 Bun+Drizzle 的组合,不是"裸 Node.js"。Bun 的 JavaScriptCore 引擎经过了大量底层优化,加上 Bun SQL 的原生 C++ 数据库驱动层,以及 Drizzle JIT 的零开销映射,三者叠加才达到这个效果。
误读二:"所有场景都应该用 Bun+Drizzle 替换 Go。" 绝对不对。这次测试的是一个相对简单的 CRUD 场景,数据库操作相对单一,没有复杂的业务逻辑。Go 的强项在于:goroutine 的轻量并发模型、标准库的极致稳定性、以及一个强大的静态二进制部署体验。这些优势在 CPU 密集型任务、复杂并发场景、大规模微服务架构中依然明显。
误读三:"Drizzle 全面超越 Prisma。" 也不对。Drizzle 在性能上有优势,但 Prisma 的迁移工具、可视化 Schema 编辑器、大型复杂关联关系的管理能力,以及更成熟的文档和社区,仍然是 Prisma 的护城河。两者各有适用场景,不是简单的替代关系。
三、核心解析:Drizzle 的 JIT Row Mappers 到底做了什么
这次 benchmark 的核心变量,是 Drizzle v1.0.0-rc.1 引入的 JIT Row Mappers(即时编译行映射器)。这是本次性能提升的根本来源。
3.1 ORM 的开销从哪来
在理解 JIT Row Mappers 之前,先理解传统 ORM 的性能瓶颈在哪里。
当你执行一条 SQL 查询:
SELECT id, email, full_name, created_at FROM users WHERE id = 1;
数据库返回的结果集,在协议层是按行传输的。每行数据本质上是一个字段数组:
Row 1: [1, "alice@example.com", "Alice Smith", "2026-01-01 10:00:00"]
Row 2: [2, "bob@example.com", "Bob Chen", "2026-01-02 11:00:00"]
...
传统 ORM(比如 Prisma、Hibernate、Sequelize)的处理流程是:
- 协议解析:从数据库协议里读取字节流,组装成行数组
- Schema 感知:查询这张表的字段定义(列名、数据类型、主键/外键关系等)
- 类型转换:把数据库原生类型(INTEGER、TIMESTAMP 等)转换成 JavaScript 运行时类型(number、Date 等)
- 对象构造:new User() 或类似操作,把每行数组映射成对象
- 关联处理:如果启用了 include/join,还要处理嵌套的对象关系
步骤 2-5 在每一条返回记录上都要执行一次,而且很多 ORM 在这几步里会大量使用反射(Reflection)或动态属性访问。这在 JavaScript 里意味着属性的动态查找(HashMap 查找)、类型守卫的类型判断、以及 for...in 循环遍历对象属性。
在高并发、大数据量查询时,这个开销是可见的。
3.2 Drizzle 的 JIT 解法
Drizzle v1.0 之前的做法,和其他 ORM 差别不大:每行数据都走一个通用的映射函数,函数内部用字段名动态查找 Schema 元数据,再逐字段做类型转换。
JIT Row Mappers 的核心思路是:在运行时,针对你的具体 Schema,生成一个专用的映射函数。
通用映射函数(传统做法):
输入:row_data, schema_metadata
输出:typed_object
代价:每行都要查 HashMap 找字段,做动态类型判断
JIT 映射函数(编译后):
输入:row_data
输出:typed_object
代价:零运行时查表,直接按固定偏移读取字段
这个"生成专用映射函数"的过程发生在什么时候?
答案是:第一次执行某张表的查询时。Drizzle 在第一次查询某张表时,会根据该表的 Schema 元信息,生成一个专门针对这张表的 TypeScript(或更准确地说是 JavaScript)函数。这个函数完全静态化,不做任何运行时查表:
// Drizzle JIT 编译器自动生成的(概念示意)
// 假设 users 表的 Schema 是 { id, email, fullName, createdAt }
// 编译器生成的这个函数只处理 users 表:
function mapUsersRow(row: unknown[]): User {
return {
id: row[0] as number, // 直接按偏移读取,无查表
email: row[1] as string, // 直接按偏移读取,无查表
fullName: row[2] as string, // 直接按偏移读取,无查表
createdAt: new Date(row[3]) // 已知是 timestamp,只需处理这一种类型
};
}
之后的每次查询,这张表的数据都会走这条"专用通道",跳过所有通用抽象层,开销接近于手写 raw SQL + 手动构建对象的组合。
3.3 为什么 Bun 上这个优化效果更明显
JIT Row Mappers 是一个通用的性能优化,在 Node.js 上也能生效。但为什么 Bun 上的效果特别突出?有几个原因:
第一,Bun 的 SQL API 更接近底层。 Bun SQL(Bun 的数据库操作 API)是 Bun 团队用 C++ 实现的,直接对接 libpg 等 C 库,没有 Node.js 的 Buffer 层和 V8 序列化层的额外开销。数据库结果的字节流可以直接映射到 JavaScript 运行时内存,减少了数据拷贝。
第二,Bun 的 JavaScriptCore 引擎对短函数执行做了更多优化。 V8 擅长的是长时间运行的 JIT 编译函数(Hot Functions),但 JIT Row Mappers 生成的是每行都调用的极短函数。JavaScriptCore 的内联缓存(Inline Caching)策略对这类短函数的优化更有效。
第三,Bun 的启动时间和内存占用更小。 这意味着在高并发下,Bun 进程本身占用的资源更少,给业务逻辑和数据库操作留出了更多空间。
三者叠加,Drizzle 的 JIT Row Mappers 在 Bun 上的效果,比在 Node.js 上更显著。
四、深度拆解:Drizzle v1.0.0-rc.1 完整变化解析
JIT Row Mappers 是这次性能提升的核心,但 v1.0.0-rc.1 还有其他三个重要变化,一并解析。
4.1 Effect v4 原生集成
Effect 是 TypeScript 生态里一个功能强大的函数式编程库,提供:
- 类型安全的错误处理(用 Result 类型替代 try/catch 的隐式错误)
- 依赖注入(用 Context 系统替代全局状态)
- 并发管理(Fiber-based 并发,支持 structured concurrency)
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;
// 直接用 yield 操作数据库,全程类型安全
const users = yield* db.select().from(usersTable);
// 错误会在 Effect 层面统一处理,无需手动 try/catch
const newUser = yield* db.insert(usersTable).values({
email: 'alice@example.com',
fullName: 'Alice Smith'
}).returning();
return newUser;
}).pipe(Effect.provide(PgClientLive));
// 执行程序
const result = await Effect.runPromise(program);
这个集成的价值在于:如果你的项目已经在用 Effect 做业务逻辑(很多类型安全的 TS 后端项目在用),现在可以把数据库层无缝接入 Effect 的函数式世界,不需要从 Effect 逃逸回 Promise 回调。
4.2 Casing API 重构(Breaking Change)
Drizzle v1.0 之前的 casing(命名风格转换)处理比较分散。有的是在 Schema 定义时指定,有的是在数据库配置层统一处理,方式不统一,导致了很多坑。
v1.0 统一了 casing API,重构后的写法更加声明式:
import * as d from 'drizzle-orm/pg-core';
// 方式一:整个 Schema 用 snake_case 数据库命名策略
// TypeScript 代码里用 camelCase,数据库里存 snake_case
const users = d.snakeCase.table('users', {
id: d.serial('id').primaryKey(),
email: d.text('email').unique(),
fullName: d.text('full_name'), // TS: fullName → DB: full_name
createdAt: d.timestamp('created_at').defaultNow(),
});
// 方式二:显式 schema 工厂,创建自定义命名的 Schema
const mySchema = d.snakeCase.schema('my_app');
const orders = mySchema.table('orders', {
id: d.serial('id').primaryKey(),
userId: d.integer('user_id').references(() => users.id),
totalAmount: d.decimal('total_amount', { precision: 10, scale: 2 }),
});
旧项目升级到 v1.0 时,如果之前用了自定义的 casing 配置,升级前一定要仔细阅读官方迁移指南,这是这次 RC 版本的 breaking change 里影响面最大的一个。
4.3 Drizzle for LLM Agents(Preview)
这是一个面向未来的特性,目前还在 preview 阶段。目标是让 LLM Agent(比如 Claude Code、Cursor Agent 等 AI 编程助手)可以直接理解和操作 Drizzle 管理的数据库结构。
具体来说,Drizzle 的 Schema 是纯 TypeScript 代码,LLM 可以直接阅读和理解,不需要额外的 Schema 解析步骤。相比之下,Prisma 的 Schema 是一种 DSL,LLM 需要额外的解析器才能理解其结构。
这个方向的意义在于:AI 编程时代,Drizzle 的"Schema 即代码、查询即 SQL"的哲学,让 AI 更容易理解和生成数据库操作代码。这是一个值得关注的方向。
五、Bun 加入 Anthropic:更大的棋局
2026年6月,还有一个重要消息被技术社区讨论:Bun 宣布加入 Anthropic。
Bun 官方博客在 v1.3.14 发布时附上了这条消息。Bun 的 JavaScript 运行时、工具链、和这次加入 Anthropic 的动作,说明了同一件事:JavaScript/TypeScript 生态已经不再只是一个"前端语言"的生态,而是一个可以真正承载服务端、CLI、数据库操作等全栈场景的生产级生态。
Bun 的加入 Anthropic 意味着什么?几个合理推测:
第一,Bun 可能会成为 Claude Code 等 Anthropic 产品的首选 JavaScript 运行时。 Claude Code 是 Anthropic 官方的 CLI 编程工具,目前官方版本用 Node.js 运行。但社区已经有了用 Bun 重写的版本(GitHub 上的 claude-code-bun 项目),体验更好、性能更佳。Bun 加入 Anthropic 后,官方支持 Bun 运行时是顺理成章的事。
第二,Anthropic 的 Claude 模型训练数据可能更偏向 JavaScript/TypeScript。 虽然 Claude 模型本身是通用的,但如果 Anthropic 在预训练阶段更多地包含 TypeScript 和 Bun 相关的代码样本,Claude 在理解和生成 TypeScript 代码时的能力会有额外提升。
第三,Bun SQL 可能会与 Anthropic 模型做更深度集成。 Drizzle+Bun 的基准测试让人们意识到,Bun 的 SQL API 已经是目前性能最好的 JavaScript 数据库操作层。如果在 Bun SQL 之上再叠一层 Claude 的自然语言理解能力,让开发者用自然语言查询数据库,这会是一个很有想象力的场景。
六、横向对比:Drizzle vs Prisma vs Go + GORM
说了这么多 Drizzle 的好,也得客观看看它在实际项目中的位置。
| 维度 | Drizzle ORM | Prisma | Go + GORM/pgx |
|---|---|---|---|
| 性能 | ★★★★★(JIT + Bun) | ★★★☆☆ | ★★★★☆ |
| 类型安全 | ★★★★★(全程推导) | ★★★★★(生成代码) | ★★★☆☆(struct tag) |
| 部署复杂度 | ★★★★★(纯 npm 包) | ★★★☆☆(CLI + 生成) | ★★★★☆(Go 二进制) |
| 迁移工具 | ★★★☆☆(基础) | ★★★★★(成熟) | ★★★★☆(专业) |
| 学习曲线 | ★★★★☆(需懂 SQL) | ★★★★★(对新手友好) | ★★★☆☆(Go 门槛) |
| 大型项目 | ★★★☆☆(复杂关联需设计) | ★★★★★(成熟生态) | ★★★★★(Go 强项) |
| 轻量场景 | ★★★★★(完美) | ★★★☆☆(偏重) | ★★★☆☆(杀鸡用牛刀) |
| AI 编程友好度 | ★★★★★(Schema 即代码) | ★★★☆☆(DSL 需解析) | ★★★★☆(结构简单) |
结论:Drizzle 非常适合以下场景:
- Bun/Deno/Cloudflare Workers 上的服务端项目
- Hono、Elysia、Luciano 等轻量框架的数据库层
- AI Agent 辅助编程的项目(Schema 可被 LLM 直接理解)
- 需要极致性能的小型服务端 API
Prisma 更适合:大型团队、需要可视化 Schema 管理、已有成熟 Prisma 生态的项目。
Go 更适合:需要编译成单一二进制、大规模微服务、需要 goroutine 并发优势的场景。
七、实战代码:从零搭建 Drizzle + Bun 服务端 API
说了这么多原理,是时候上代码了。来看一个完整的实战案例:用一个 Bun HTTP 服务,加 Drizzle ORM(JIT row mappers),实现一个带完整 CRUD 的用户管理 API。
7.1 项目初始化
# 安装 Bun
curl -fsSL https://bun.sh/install | bash
# 创建项目
mkdir drizzle-bun-api && cd drizzle-bun-api
bun init
# 安装依赖
bun add drizzle-orm postgres
bun add -d drizzle-kit @types/pg
7.2 数据库 Schema 定义
// src/db/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').unique().notNull(),
fullName: text('full_name').notNull(),
age: integer('age'),
createdAt: timestamp('created_at').defaultNow(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
7.3 数据库连接(JIT mappers 已默认启用)
// src/db/index.ts
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const connectionString = process.env.DATABASE_URL!;
// postgres-js 实例,用于执行查询
const client = postgres(connectionString);
// drizzle 实例,传入 schema 以启用类型推导
// JIT Row Mappers 在第一次查询时自动编译
export const db = drizzle(client, { schema });
7.4 CRUD API 实现
// src/routes/users.ts
import { Hono } from 'hono';
import { eq } from 'drizzle-orm';
import { db } from '../db';
import { users } from '../db/schema';
import type { NewUser, User } from '../db/schema';
const app = new Hono();
// GET /users - 获取所有用户
app.get('/', async (c) => {
const allUsers = await db.select().from(users);
return c.json({ data: allUsers, count: allUsers.length });
});
// GET /users/:id - 获取单个用户
app.get('/:id', async (c) => {
const id = Number(c.req.param('id'));
const [user] = await db.select().from(users).where(eq(users.id, id));
if (!user) {
return c.json({ error: 'User not found' }, 404);
}
return c.json({ data: user });
});
// POST /users - 创建用户
app.post('/', async (c) => {
const body = await c.req.json<Partial<NewUser>>();
if (!body.email || !body.fullName) {
return c.json({ error: 'email and fullName are required' }, 400);
}
const [newUser] = await db.insert(users).values(body).returning();
return c.json({ data: newUser }, 201);
});
// PUT /users/:id - 更新用户
app.put('/:id', async (c) => {
const id = Number(c.req.param('id'));
const body = await c.req.json<Partial<User>>();
const [updated] = await db.update(users)
.set(body)
.where(eq(users.id, id))
.returning();
if (!updated) {
return c.json({ error: 'User not found' }, 404);
}
return c.json({ data: updated });
});
// DELETE /users/:id - 删除用户
app.delete('/:id', async (c) => {
const id = Number(c.req.param('id'));
const [deleted] = await db.delete(users)
.where(eq(users.id, id))
.returning();
if (!deleted) {
return c.json({ error: 'User not found' }, 404);
}
return c.json({ data: deleted, message: 'User deleted' });
});
export default app;
7.5 启动服务
// src/index.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import usersRouter from './routes/users';
const app = new Hono();
// CORS 支持
app.use('/*', cors({
origin: '*',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
}));
// 路由
app.route('/users', usersRouter);
// 健康检查
app.get('/health', (c) => c.json({ status: 'ok', runtime: 'bun' }));
// Bun 原生 HTTP 服务器
const port = parseInt(process.env.PORT || '3000');
console.log(`🚀 Server running on http://localhost:${port}`);
export default {
port,
fetch: app.fetch,
};
运行方式:
# 开发环境
bun run src/index.ts
# 生产环境(Bun 编译成单文件)
bun build src/index.ts --outfile ./dist/server --target bun
./dist/server
八、性能优化:榨干 Drizzle + Bun 组合的性能
拿到这套技术栈,怎么让它跑得更快?有几个经过验证的优化手段。
8.1 连接池配置
PostgreSQL 的默认连接池大小对高并发场景往往不够。Drizzle 的连接池配置:
// 连接池配置示例
const client = postgres(connectionString, {
max: 20, // 最大连接数,根据服务器配置调整
idle_timeout: 20, // 空闲连接保留时间(秒)
connect_timeout: 10, // 连接超时(秒)
max_lifetime: 60 * 30, // 单个连接最大生命周期(秒)
});
8.2 批量操作减少网络往返
不要在循环里逐条插入,大量插入用 Drizzle 的批量 API:
// ❌ 错误:N 次数据库往返
for (const user of manyUsers) {
await db.insert(users).values(user);
}
// ✅ 正确:单次数据库往返
await db.insert(users).values(manyUsers);
Drizzle 在底层会把批量 insert 合并成一条 SQL:
INSERT INTO users (email, full_name, age) VALUES
('a@ex.com', 'Alice', 25),
('b@ex.com', 'Bob', 30),
('c@ex.com', 'Carol', 28),
-- 一次性插入所有记录
8.3 选择性字段查询
不需要所有字段时,用 select 明确指定字段,减少数据传输量:
// ❌ 查询所有字段
const all = await db.select().from(users);
// ✅ 只查询需要的字段
const emails = await db
.select({ email: users.email, name: users.fullName })
.from(users);
8.4 分页查询的正确姿势
// 基于游标的分页(推荐,比 OFFSET 分页在大数据集上更快)
const PAGE_SIZE = 20;
const lastSeenId = Number(c.req.query('cursor') || 0);
const rows = await db
.select()
.from(users)
.where(gt(users.id, lastSeenId))
.limit(PAGE_SIZE)
.orderBy(users.id);
九、迁移指南:从 Prisma 到 Drizzle 的实战步骤
如果你现在在用 Prisma,想迁移到 Drizzle(或者在新项目里选 Drizzle),这里提供一条经过验证的迁移路径。
9.1 Schema 转换对照
// Prisma schema.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
fullName String
createdAt DateTime @default(now())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
userId Int
author User @relation(fields: [userId], references: [id])
}
对应 Drizzle Schema:
// Drizzle 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').unique().notNull(),
fullName: text('full_name').notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
userId: integer('user_id').references(() => users.id),
});
9.2 查询 API 对照
// Prisma: 创建用户
const user = await prisma.user.create({
data: { email: 'test@ex.com', fullName: 'Test' },
include: { posts: true },
});
// Drizzle: 创建用户 + 关联查询
const [user] = await db.insert(users).values({
email: 'test@ex.com',
fullName: 'Test',
}).returning();
const userPosts = await db.select().from(posts).where(eq(posts.userId, user.id));
9.3 迁移工具链
# 生成迁移 SQL
bunx drizzle-kit generate
# 应用迁移到数据库
bunx drizzle-kit migrate
# 在开发时推送到数据库(不需要生成迁移文件)
bunx drizzle-kit push
十、总结:2026 年的 TypeScript 后端新格局
回到最初的问题:Drizzle ORM 让 Bun 跑出比 Go 更低的延迟,这件事到底意味着什么?
它意味着 JavaScript/TypeScript 生态在后端领域,已经从一个"能跑"的状态,进化到了一个"可以认真对比性能"的状态。
这个变化的发生,不是因为某个单一技术突破,而是多个层面的进步叠加的结果:
- 运行时层:Bun 的 JavaScriptCore + C++ 原生模块架构,把 JavaScript 运行时的性能天花板往上推了一大截
- 数据库驱动层:Bun SQL 的原生 PostgreSQL/MySQL 实现,跳过了 Node.js 的 Buffer 层和 V8 序列化层
- ORM 层:Drizzle 的 JIT Row Mappers,把传统 ORM 里最大的性能瓶颈(行映射开销)几乎压缩到零
- 语言层:TypeScript 的类型系统让这些复杂的编译时优化成为可能,而不会牺牲开发体验
再加上 Bun 加入 Anthropic 这个信号,2026 年的 TypeScript 后端生态,正在经历一次从"能用"到"好用"再到"高性能"的跃迁。
对于开发者来说,这意味着什么?
如果你在做一个新的服务端项目,特别是轻量 API、微服务、或边缘计算场景,现在有一个以前没有过的技术组合值得认真考虑:Bun + Hono/Elysia + Drizzle。三者组合:极快的启动时间、极低的内存占用、TypeScript 全程类型安全、AI 编程助手可以直接理解代码结构。
如果你已经在用 Go 或 Java 等 JVM 语言,也不必焦虑。这些语言在 CPU 密集型任务、大规模并发、微服务治理等场景下依然有不可替代的优势。Drizzle+Bun 的基准测试只是在某些特定场景下展现了优势,并不能推翻后端开发中其他维度的权衡。
一件事是确定的:TypeScript 全栈的门槛在2026年降到了历史最低,性能上限却升到了历史最高。 这个组合值得关注,更值得动手试一试。
选题来源:GitHub Trending + Drizzle ORM v1.0.0-rc.1 发布公告
技术审校:基准测试数据来自 Drizzle 官方发布(2026年6月),Go 对比侧为 v1.25.5 标准库