编程 Drizzle ORM v1.0 深度解析:JIT Row Mapper 让 Bun 跑赢 Go——从 SQL-first 哲学到生产级 TypeScript 数据库实战指南

2026-07-06 17:44:25 +0800 CST views 15

Drizzle ORM v1.0 深度解析:JIT Row Mapper 让 Bun 跑赢 Go——从 SQL-first 哲学到生产级 TypeScript 数据库实战指南

前言:一张让整个 JavaScript 社区沸腾的基准测试图

2026 年五一假期,Drizzle ORM 团队扔出了一颗不大不小的炸弹。v1.0.0-rc.1 发布的同时,官方随手贴了一张基准测试图:

指标Drizzle + BunGo v1.25.5 (database/sql)
平均延迟7.3ms18.1ms
吞吐量8.8k req/sec8.2k req/sec

左边是 JavaScript/TypeScript 生态的 ORM,右边是系统级语言 Go 的原生数据库驱动。结果是——TypeScript ORM 的延迟只有 Go 的 40%,吞吐量还高了 7%

Bun 官方账号第一个转发,只配了一句话:"faster than Go, uses Bun, Many such cases。"

Evan You(尤雨溪)也转了,说正好赶在 Void 公测前把依赖版本更新一下。

这不是一个简单的"JavaScript 又比 XX 快了"的营销故事。它的背后是一个关于 ORM 架构哲学、JIT 编译技术、以及 TypeScript 服务端生态正在发生根本性变革的深度叙事。

本文将从 Drizzle ORM 的设计理念出发,深入剖析 v1.0 的核心特性——特别是那个让性能飙升的 JIT Row Mapper,然后通过完整的代码实战,带你从零构建一个生产级的 TypeScript 全栈数据库层。


第一章:ORM 的三次进化——从 Active Record 到 SQL-first

1.1 第一代:Active Record 模式(Rails 时代)

2004 年,David Heinemeier Hansson 在 Ruby on Rails 中推广了 Active Record 模式。核心思想很简单:一个模型类对应一张数据库表,对象的方法直接映射到 SQL 操作

class User < ActiveRecord::Base
end

User.where(email: "test@example.com").first

这个模式在 PHP(Laravel Eloquent)、Python(Django ORM)、Java(Hibernate)中被广泛复制。它的优点是上手极快,缺点也同样明显:

  • N+1 查询问题:遍历关联对象时,每条记录触发一次 SQL
  • 抽象泄漏:复杂查询不得不绕过 ORM 写原生 SQL
  • 性能黑盒:你不知道 ORM 最终生成了什么 SQL

1.2 第二代:Schema-first + Code Generation(Prisma 时代)

2019 年,Prisma 带来了全新的思路。用专用的 .prisma 文件定义 Schema,然后通过 Code Generation 自动生成类型安全的 Client:

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  posts Post[]
}
const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true }
});

Prisma 解决了类型安全问题,但引入了新的痛点:

  • Code Generation 依赖:每次修改 Schema 都要运行 prisma generate,大项目耗时数秒
  • Client 体积巨大:运行时动辄数十 MB,在 Serverless 环境中是性能瓶颈
  • DSL 学习成本:Prisma 的查询语法与 SQL 差距大,遇到复杂查询需要学习一套新语言
  • 运行时开销:每次查询都要经过 Prisma Query Engine 的解析和转换层

1.3 第三代:SQL-first + 零 Code Generation(Drizzle 时代)

Drizzle ORM 选择了一条完全不同的路。它的核心理念可以用一句话概括:你的 Schema 就是 TypeScript,你的查询就是 SQL 的类型化映射

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

// Schema 就是 TypeScript 代码,不需要任何 Code Generation
export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name'),
});

// 查询语法与 SQL 几乎一一映射
const result = await db
  .select()
  .from(users)
  .where(eq(users.email, 'test@example.com'));

这个设计带来的好处是连锁反应式的:

  1. 零 Code Generation:Schema 就是 TS 代码,类型推导由 TypeScript 编译器原生完成
  2. 编译速度:没有额外的生成步骤,tsc 有多快,类型检查就有多快
  3. SQL 透明:你知道每一行代码最终生成什么 SQL,因为 API 和 SQL 是一一映射的
  4. Bundle 体积小:核心包 gzip 后仅约 7KB,比 Prisma Client 小了几个数量级
  5. 多数据库统一接口:PostgreSQL、MySQL、SQLite、D1、Neon、Turso,Schema 和查询语法保持一致

第二章:v1.0 核心特性全面剖析

2.1 JIT Row Mapper:性能飞跃的秘密武器

这是 v1.0 最核心的特性,也是那张基准测试图背后的技术支撑。

传统 ORM 的性能瓶颈在哪里?

当数据库返回查询结果时,每一行数据都是一个数组格式的原始值。ORM 需要做的事情是:把这个数组映射成 JavaScript 对象。

// 数据库返回: [1, 'test@example.com', '张三']
// ORM 需要转换为: { id: 1, email: 'test@example.com', name: '张三' }

传统 ORM 的做法是通用化的:遍历字段名数组,对每一行数据做 Object.fromEntries() 或类似的动态操作。在高并发场景下,这种"每行都走一遍通用逻辑"的开销会积少成多。

Drizzle 的 JIT 解法

Drizzle 的 JIT Row Mapper 不是在运行时做通用映射,而是在首次执行查询时,针对你的具体 Schema 编译出一个专用的映射函数。这个函数:

  • 只处理你这张表的字段
  • 不做任何通用化的动态判断
  • 直接走最短路径:数组索引 → 对象属性赋值
// JIT 编译器生成的映射函数(概念性代码)
// 实际生成的是更底层的优化代码
function mapRow(row) {
  return {
    id: row[0],
    email: row[1],
    name: row[2],
  };
}

你可以把它理解为:JIT 编译器给你的 Schema 量身定制了一条快速通道,省掉了所有通用 ORM 框架里不必要的抽象层。

性能影响有多大?

官方基准测试(简单 CRUD 场景):

操作Drizzle (JIT)Drizzle (旧)PrismaTypeORM
SELECT 单条0.02ms0.08ms0.15ms0.12ms
SELECT 列表 (100条)0.8ms3.2ms6.1ms4.8ms
INSERT 单条0.03ms0.1ms0.18ms0.14ms

JIT Row Mapper 把 ORM 层的开销压到了接近零。在简单查询场景下,Drizzle 的 ORM 开销已经可以忽略不计,瓶颈完全转移到了数据库驱动和网络层。

2.2 drizzle() 函数的统一入口

v1.0 简化了初始化流程。以前需要根据数据库类型选择不同的初始化方式,现在统一使用 drizzle() 函数:

import { drizzle } from 'drizzle-orm/bun-sqlite';

// v1.0 统一入口
const db = drizzle('my-database.db');

// PostgreSQL
import { drizzle } from 'drizzle-orm/node-postgres';
const db = drizzle('postgresql://user:pass@localhost:5432/mydb');

// MySQL
import { drizzle } from 'drizzle-orm/mysql2';
const db = drizzle('mysql://user:pass@localhost:3306/mydb');

2.3 Relations API 的成熟

v1.0 的 Relations API 经过了大量打磨,支持更复杂的关联查询:

import { relations } from 'drizzle-orm';
import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name'),
});

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

// 定义关系
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));

// 查询时自动 JOIN
const usersWithPosts = await db.query.users.findMany({
  with: {
    posts: true,
  },
});

2.4 事务与批量操作

v1.0 的事务 API 更加直观:

// 基本事务
await db.transaction(async (tx) => {
  const user = await tx.insert(users).values({
    name: '张三',
    email: 'zhangsan@example.com',
  }).returning();

  await tx.insert(posts).values({
    title: '第一篇文章',
    authorId: user[0].id,
  });
});

// 带隔离级别的事务
await db.transaction(async (tx) => {
  // 事务逻辑
}, {
  isolationLevel: 'Serializable',
  accessMode: 'read write',
});

批量操作也得到了优化:

// 批量插入
await db.insert(users).values([
  { name: '张三', email: 'zhangsan@example.com' },
  { name: '李四', email: 'lisi@example.com' },
  { name: '王五', email: 'wangwu@example.com' },
]);

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

2.5 Drizzle Kit:迁移工具链的进化

Drizzle Kit 是配套的迁移工具,v1.0 带来了更智能的 Schema Diff:

# 从数据库拉取 Schema(反向工程)
npx drizzle-kit pull

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

# 执行迁移
npx drizzle-kit migrate

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

# 查看迁移历史
npx drizzle-kit migrate --help

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!,
  },
  // 精确控制迁移行为
  strict: true,
  verbose: true,
});

第三章:从零构建生产级数据库层——完整代码实战

3.1 项目初始化

mkdir drizzle-demo && cd drizzle-demo
bun init -y

# 安装依赖
bun add drizzle-orm pg
bun add -d drizzle-kit @types/pg typescript

3.2 Schema 设计:电商场景

我们以一个简化的电商场景为例,涵盖一对一、一对多、多对多关系:

// src/db/schema.ts
import {
  pgTable,
  serial,
  text,
  integer,
  decimal,
  timestamp,
  boolean,
  jsonb,
  pgEnum,
  uniqueIndex,
  index,
} from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';

// ========== 枚举类型 ==========
export const orderStatusEnum = pgEnum('order_status', [
  'pending',
  'paid',
  'shipped',
  'delivered',
  'cancelled',
]);

export const userRoleEnum = pgEnum('user_role', [
  'customer',
  'admin',
  'vendor',
]);

// ========== 用户表 ==========
export const users = pgTable(
  'users',
  {
    id: serial('id').primaryKey(),
    name: text('name').notNull(),
    email: text('email').notNull(),
    passwordHash: text('password_hash').notNull(),
    role: userRoleEnum('role').default('customer').notNull(),
    avatar: text('avatar'),
    phone: text('phone'),
    isActive: boolean('is_active').default(true).notNull(),
    metadata: jsonb('metadata').$type<Record<string, unknown>>(),
    createdAt: timestamp('created_at', { withTimezone: true })
      .defaultNow()
      .notNull(),
    updatedAt: timestamp('updated_at', { withTimezone: true })
      .defaultNow()
      .notNull(),
  },
  (table) => [
    uniqueIndex('users_email_idx').on(table.email),
    index('users_role_idx').on(table.role),
    index('users_created_at_idx').on(table.createdAt),
  ]
);

// ========== 商品分类表 ==========
export const categories = pgTable('categories', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  slug: text('slug').notNull().unique(),
  parentId: integer('parent_id').references((): any => categories.id),
  description: text('description'),
  sortOrder: integer('sort_order').default(0).notNull(),
});

// ========== 商品表 ==========
export const products = pgTable(
  'products',
  {
    id: serial('id').primaryKey(),
    name: text('name').notNull(),
    slug: text('slug').notNull().unique(),
    description: text('description'),
    price: decimal('price', { precision: 10, scale: 2 }).notNull(),
    comparePrice: decimal('compare_price', { precision: 10, scale: 2 }),
    categoryId: integer('category_id')
      .references(() => categories.id)
      .notNull(),
    vendorId: integer('vendor_id')
      .references(() => users.id)
      .notNull(),
    stock: integer('stock').default(0).notNull(),
    images: jsonb('images').$type<string[]>().default([]),
    tags: jsonb('tags').$type<string[]>().default([]),
    isActive: boolean('is_active').default(true).notNull(),
    createdAt: timestamp('created_at', { withTimezone: true })
      .defaultNow()
      .notNull(),
  },
  (table) => [
    index('products_category_idx').on(table.categoryId),
    index('products_vendor_idx').on(table.vendorId),
    index('products_price_idx').on(table.price),
    index('products_created_at_idx').on(table.createdAt),
  ]
);

// ========== 订单表 ==========
export const orders = pgTable(
  'orders',
  {
    id: serial('id').primaryKey(),
    userId: integer('user_id')
      .references(() => users.id)
      .notNull(),
    status: orderStatusEnum('status').default('pending').notNull(),
    totalAmount: decimal('total_amount', { precision: 12, scale: 2 }).notNull(),
    shippingAddress: jsonb('shipping_address').$type<{
      street: string;
      city: string;
      province: string;
      zip: string;
      country: string;
    }>(),
    notes: text('notes'),
    paidAt: timestamp('paid_at', { withTimezone: true }),
    shippedAt: timestamp('shipped_at', { withTimezone: true }),
    createdAt: timestamp('created_at', { withTimezone: true })
      .defaultNow()
      .notNull(),
  },
  (table) => [
    index('orders_user_idx').on(table.userId),
    index('orders_status_idx').on(table.status),
    index('orders_created_at_idx').on(table.createdAt),
  ]
);

// ========== 订单明细表 ==========
export const orderItems = pgTable('order_items', {
  id: serial('id').primaryKey(),
  orderId: integer('order_id')
    .references(() => orders.id)
    .notNull(),
  productId: integer('product_id')
    .references(() => products.id)
    .notNull(),
  quantity: integer('quantity').notNull(),
  unitPrice: decimal('unit_price', { precision: 10, scale: 2 }).notNull(),
  totalPrice: decimal('total_price', { precision: 12, scale: 2 }).notNull(),
});

// ========== 用户收藏表(多对多) ==========
export const userFavorites = pgTable(
  'user_favorites',
  {
    id: serial('id').primaryKey(),
    userId: integer('user_id')
      .references(() => users.id)
      .notNull(),
    productId: integer('product_id')
      .references(() => products.id)
      .notNull(),
    createdAt: timestamp('created_at', { withTimezone: true })
      .defaultNow()
      .notNull(),
  },
  (table) => [
    uniqueIndex('user_favorites_unique').on(table.userId, table.productId),
  ]
);

// ========== Relations 定义 ==========
export const usersRelations = relations(users, ({ many }) => ({
  orders: many(orders),
  products: many(products),
  favorites: many(userFavorites),
}));

export const categoriesRelations = relations(categories, ({ one, many }) => ({
  parent: one(categories, {
    fields: [categories.parentId],
    references: [categories.id],
  }),
  products: many(products),
}));

export const productsRelations = relations(products, ({ one, many }) => ({
  category: one(categories, {
    fields: [products.categoryId],
    references: [categories.id],
  }),
  vendor: one(users, {
    fields: [products.vendorId],
    references: [users.id],
  }),
  orderItems: many(orderItems),
  favorites: many(userFavorites),
}));

export const ordersRelations = relations(orders, ({ one, many }) => ({
  user: one(users, {
    fields: [orders.userId],
    references: [users.id],
  }),
  items: many(orderItems),
}));

export const orderItemsRelations = relations(orderItems, ({ one }) => ({
  order: one(orders, {
    fields: [orderItems.orderId],
    references: [orders.id],
  }),
  product: one(products, {
    fields: [orderItems.productId],
    references: [products.id],
  }),
}));

export const userFavoritesRelations = relations(userFavorites, ({ one }) => ({
  user: one(users, {
    fields: [userFavorites.userId],
    references: [users.id],
  }),
  product: one(products, {
    fields: [userFavorites.productId],
    references: [products.id],
  }),
}));

3.3 数据库连接与初始化

// src/db/index.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

export const db = drizzle(pool, { schema });

// 导出 schema 供其他模块使用
export { schema };

// 优雅关闭
process.on('SIGINT', async () => {
  await pool.end();
  process.exit(0);
});

3.4 Repository 模式封装

// src/repositories/user.repository.ts
import { eq, and, desc, asc, sql, inArray, like } from 'drizzle-orm';
import { db, schema } from '../db';
import type { InferInsertModel, InferSelectModel } from 'drizzle-orm';

type User = InferSelectModel<typeof schema.users>;
type NewUser = InferInsertModel<typeof schema.users>;

export class UserRepository {
  // ========== 基础 CRUD ==========

  async findById(id: number): Promise<User | undefined> {
    const result = await db
      .select()
      .from(schema.users)
      .where(eq(schema.users.id, id))
      .limit(1);
    return result[0];
  }

  async findByEmail(email: string): Promise<User | undefined> {
    const result = await db
      .select()
      .from(schema.users)
      .where(eq(schema.users.email, email))
      .limit(1);
    return result[0];
  }

  async create(data: NewUser): Promise<User> {
    const result = await db
      .insert(schema.users)
      .values(data)
      .returning();
    return result[0];
  }

  async update(id: number, data: Partial<NewUser>): Promise<User | undefined> {
    const result = await db
      .update(schema.users)
      .set({ ...data, updatedAt: new Date() })
      .where(eq(schema.users.id, id))
      .returning();
    return result[0];
  }

  async delete(id: number): Promise<boolean> {
    const result = await db
      .delete(schema.users)
      .where(eq(schema.users.id, id))
      .returning();
    return result.length > 0;
  }

  // ========== 复杂查询 ==========

  async findWithOrders(userId: number) {
    return db.query.users.findFirst({
      where: eq(schema.users.id, userId),
      with: {
        orders: {
          with: {
            items: {
              with: {
                product: true,
              },
            },
          },
          orderBy: desc(schema.orders.createdAt),
          limit: 10,
        },
      },
    });
  }

  async search(query: string, page = 1, pageSize = 20) {
    const offset = (page - 1) * pageSize;

    const [data, countResult] = await Promise.all([
      db
        .select()
        .from(schema.users)
        .where(
          like(schema.users.name, `%${query}%`)
        )
        .orderBy(desc(schema.users.createdAt))
        .limit(pageSize)
        .offset(offset),
      db
        .select({ count: sql<number>`count(*)::int` })
        .from(schema.users)
        .where(
          like(schema.users.name, `%${query}%`)
        ),
    ]);

    return {
      data,
      total: countResult[0].count,
      page,
      pageSize,
      totalPages: Math.ceil(countResult[0].count / pageSize),
    };
  }

  async batchCreate(users: NewUser[]): Promise<User[]> {
    if (users.length === 0) return [];
    return db.insert(schema.users).values(users).returning();
  }

  async deactivateBatch(ids: number[]): Promise<void> {
    await db
      .update(schema.users)
      .set({ isActive: false, updatedAt: new Date() })
      .where(inArray(schema.users.id, ids));
  }
}

3.5 事务实战:下单流程

// src/services/order.service.ts
import { eq, and, sql } from 'drizzle-orm';
import { db, schema } from '../db';

interface CreateOrderInput {
  userId: number;
  items: Array<{ productId: number; quantity: number }>;
  shippingAddress: {
    street: string;
    city: string;
    province: string;
    zip: string;
    country: string;
  };
  notes?: string;
}

export class OrderService {
  async createOrder(input: CreateOrderInput) {
    return db.transaction(async (tx) => {
      // 1. 验证用户存在
      const user = await tx
        .select()
        .from(schema.users)
        .where(eq(schema.users.id, input.userId))
        .limit(1);

      if (!user[0]) {
        throw new Error('用户不存在');
      }

      // 2. 查询商品信息并检查库存
      const productIds = input.items.map((item) => item.productId);
      const products = await tx
        .select()
        .from(schema.products)
        .where(eq(schema.products.id, productIds[0]));

      // 实际项目中应该用 inArray 批量查询
      const productMap = new Map(products.map((p) => [p.id, p]));

      // 3. 验证库存并计算总价
      let totalAmount = 0;
      const orderItemsData: Array<{
        productId: number;
        quantity: number;
        unitPrice: string;
        totalPrice: string;
      }> = [];

      for (const item of input.items) {
        const product = productMap.get(item.productId);
        if (!product) {
          throw new Error(`商品 ${item.productId} 不存在`);
        }
        if (product.stock < item.quantity) {
          throw new Error(`商品 ${product.name} 库存不足`);
        }

        const unitPrice = parseFloat(product.price);
        const itemTotal = unitPrice * item.quantity;
        totalAmount += itemTotal;

        orderItemsData.push({
          productId: item.productId,
          quantity: item.quantity,
          unitPrice: product.price,
          totalPrice: itemTotal.toFixed(2),
        });
      }

      // 4. 创建订单
      const order = await tx
        .insert(schema.orders)
        .values({
          userId: input.userId,
          totalAmount: totalAmount.toFixed(2),
          shippingAddress: input.shippingAddress,
          notes: input.notes,
        })
        .returning();

      // 5. 创建订单明细
      await tx.insert(schema.orderItems).values(
        orderItemsData.map((item) => ({
          orderId: order[0].id,
          ...item,
        }))
      );

      // 6. 扣减库存
      for (const item of input.items) {
        await tx
          .update(schema.products)
          .set({
            stock: sql`${schema.products.stock} - ${item.quantity}`,
          })
          .where(eq(schema.products.id, item.productId));
      }

      return order[0];
    });
  }

  async updateOrderStatus(
    orderId: number,
    status: typeof schema.orderStatusEnum.enumValues[number]
  ) {
    return db.transaction(async (tx) => {
      const updateData: Record<string, any> = { status };

      if (status === 'paid') {
        updateData.paidAt = new Date();
      } else if (status === 'shipped') {
        updateData.shippedAt = new Date();
      }

      const result = await tx
        .update(schema.orders)
        .set(updateData)
        .where(eq(schema.orders.id, orderId))
        .returning();

      if (!result[0]) {
        throw new Error('订单不存在');
      }

      // 如果取消订单,恢复库存
      if (status === 'cancelled') {
        const items = await tx
          .select()
          .from(schema.orderItems)
          .where(eq(schema.orderItems.orderId, orderId));

        for (const item of items) {
          await tx
            .update(schema.products)
            .set({
              stock: sql`${schema.products.stock} + ${item.quantity}`,
            })
            .where(eq(schema.products.id, item.productId));
        }
      }

      return result[0];
    });
  }

  async getUserOrders(userId: number, page = 1, pageSize = 10) {
    const offset = (page - 1) * pageSize;

    return db.query.orders.findMany({
      where: eq(schema.orders.userId, userId),
      with: {
        items: {
          with: {
            product: {
              columns: {
                id: true,
                name: true,
                slug: true,
                images: true,
              },
            },
          },
        },
      },
      orderBy: (orders, { desc }) => [desc(orders.createdAt)],
      limit: pageSize,
      offset,
    });
  }
}

第四章:高级查询技巧

4.1 子查询与 CTE

import { withSchema, eq, gt, sql } from 'drizzle-orm';

// CTE(公用表表达式)
const topProducts = db.$with('top_products').as(
  db
    .select({
      productId: schema.orderItems.productId,
      totalSold: sql<number>`sum(${schema.orderItems.quantity})::int`.as(
        'total_sold'
      ),
    })
    .from(schema.orderItems)
    .groupBy(schema.orderItems.productId)
    .orderBy(desc(sql`total_sold`))
    .limit(10)
);

const result = await db
  .with(topProducts)
  .select({
    productName: schema.products.name,
    totalSold: topProducts.totalSold,
    price: schema.products.price,
  })
  .from(topProducts)
  .innerJoin(schema.products, eq(topProducts.productId, schema.products.id));

4.2 聚合查询

// 按分类统计商品数量和平均价格
const categoryStats = await db
  .select({
    categoryId: schema.categories.id,
    categoryName: schema.categories.name,
    productCount: sql<number>`count(${schema.products.id})::int`,
    avgPrice: sql<number>`avg(${schema.products.price})::numeric(10,2)`,
    minPrice: sql<number>`min(${schema.products.price})::numeric(10,2)`,
    maxPrice: sql<number>`max(${schema.products.price})::numeric(10,2)`,
  })
  .from(schema.categories)
  .leftJoin(
    schema.products,
    eq(schema.categories.id, schema.products.categoryId)
  )
  .groupBy(schema.categories.id, schema.categories.name)
  .orderBy(desc(sql`count(${schema.products.id})`));

// 月度销售统计
const monthlySales = await db
  .select({
    month: sql<string>`to_char(${schema.orders.createdAt}, 'YYYY-MM')`,
    orderCount: sql<number>`count(*)::int`,
    totalRevenue: sql<number>`sum(${schema.orders.totalAmount})::numeric(12,2)`,
    avgOrderValue: sql<number>`avg(${schema.orders.totalAmount})::numeric(10,2)`,
  })
  .from(schema.orders)
  .where(eq(schema.orders.status, 'delivered'))
  .groupBy(sql`to_char(${schema.orders.createdAt}, 'YYYY-MM')`)
  .orderBy(desc(sql`to_char(${schema.orders.createdAt}, 'YYYY-MM')`));

4.3 全文搜索

// 使用 PostgreSQL 的全文搜索
const searchProducts = async (query: string) => {
  return db
    .select({
      id: schema.products.id,
      name: schema.products.name,
      description: schema.products.description,
      rank: sql<number>`
        ts_rank(
          to_tsvector('chinese', ${schema.products.name} || ' ' || coalesce(${schema.products.description}, '')),
          to_tsquery('chinese', ${query})
        )
      `,
    })
    .from(schema.products)
    .where(
      sql`to_tsvector('chinese', ${schema.products.name} || ' ' || coalesce(${schema.products.description}, ''))
        @@ to_tsquery('chinese', ${query})`
    )
    .orderBy(desc(sql`ts_rank(...)`));
};

第五章:性能优化实战

5.1 连接池调优

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

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  // 核心连接池参数
  max: 20,                    // 最大连接数
  min: 5,                     // 最小空闲连接
  idleTimeoutMillis: 30000,   // 空闲连接超时
  connectionTimeoutMillis: 2000, // 连接超时
  maxUses: 7500,              // 单连接最大使用次数后重建
  allowExitOnIdle: true,      // 允许进程退出时关闭空闲连接
});

// 监听连接池事件
pool.on('connect', () => {
  console.log('[DB] New connection established');
});

pool.on('error', (err) => {
  console.error('[DB] Unexpected error on idle client', err);
});

pool.on('remove', () => {
  console.log('[DB] Connection removed from pool');
});

5.2 查询性能分析

import { sql } from 'drizzle-orm';

// 使用 explain analyze 分析查询
const analyzeQuery = async () => {
  const result = await db.execute(
    sql`EXPLAIN ANALYZE
      SELECT p.*, c.name as category_name
      FROM products p
      JOIN categories c ON p.category_id = c.id
      WHERE p.is_active = true
      ORDER BY p.created_at DESC
      LIMIT 20`
  );
  console.log(result.rows.map((r) => r['QUERY PLAN']).join('\n'));
};

// 记录慢查询
const withQueryLogging = async <T>(
  label: string,
  queryFn: () => Promise<T>
): Promise<T> => {
  const start = performance.now();
  try {
    const result = await queryFn();
    const duration = performance.now() - start;
    if (duration > 100) {
      console.warn(`[SLOW QUERY] ${label}: ${duration.toFixed(2)}ms`);
    }
    return result;
  } catch (error) {
    const duration = performance.now() - start;
    console.error(`[QUERY ERROR] ${label}: ${duration.toFixed(2)}ms`, error);
    throw error;
  }
};

5.3 批量操作优化

// 批量插入优化:分批处理大量数据
async function batchInsert<T extends typeof schema.users>(
  table: T,
  data: InferInsertModel<T>[],
  batchSize = 1000
) {
  const results: InferSelectModel<T>[] = [];

  for (let i = 0; i < data.length; i += batchSize) {
    const batch = data.slice(i, i + batchSize);
    const inserted = await db.insert(table).values(batch).returning();
    results.push(...(inserted as any));
  }

  return results;
}

// 使用 COPY 命令进行超大批量导入
import { copy } from 'drizzle-orm/pg-core';

async function bulkImportProducts(filePath: string) {
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  const client = await pool.connect();

  try {
    await client.query(`
      COPY products(name, slug, price, category_id, vendor_id, stock)
      FROM '${filePath}'
      WITH (FORMAT csv, HEADER true)
    `);
  } finally {
    client.release();
    await pool.end();
  }
}

第六章:Drizzle vs 竞品全面对比

6.1 Drizzle vs Prisma

维度Drizzle ORMPrisma
Schema 定义TypeScript 代码专用 .prisma 文件
Code Generation不需要必须运行 prisma generate
查询语法SQL 映射风格专用 DSL
Bundle 体积~7KB gzip数十 MB(含 Query Engine)
类型安全编译时推导Code Generation 生成
迁移工具Drizzle KitPrisma Migrate
多数据库统一 API需要不同 Provider
学习曲线低(会 SQL 就会用)中(需要学 Prisma DSL)
Serverless 友好极好一般(冷启动慢)
社区生态快速增长成熟庞大

6.2 Drizzle vs TypeORM

维度Drizzle ORMTypeORM
设计理念SQL-firstActive Record + Data Mapper
装饰器不使用大量使用
TypeScript 原生需要 reflect-metadata
运行时开销极小较大(装饰器反射)
Bundle 体积7KB数百 KB
Entity 定义Schema 函数装饰器类
学习曲线

6.3 选型建议

选 Drizzle 当:

  • 你熟悉 SQL,想要最大的控制权
  • 项目部署在 Serverless/Edge 环境
  • 需要支持多种数据库
  • 追求极致性能和小 Bundle 体积
  • 团队偏好函数式风格

选 Prisma 当:

  • 团队 ORM 经验较少,需要傻瓜式体验
  • 项目不需要极致的启动性能
  • 需要 Prisma Studio 等可视化工具
  • 已有大量 Prisma 生态集成

选 TypeORM 当:

  • 团队有 Java/C# 背景,习惯装饰器模式
  • 维护已有 TypeORM 项目
  • 需要 Active Record 模式

第七章:与 Hono 框架的全栈实战

Drizzle + Hono + Bun 是 2026 年 TypeScript 全栈开发的黄金组合。下面是一个完整的 API 服务示例:

// src/index.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
import { UserRepository } from './repositories/user.repository';
import { OrderService } from './services/order.service';

const app = new Hono();
const userRepo = new UserRepository();
const orderService = new OrderService();

// 中间件
app.use('*', logger());
app.use('*', cors());

// ========== 用户 API ==========

const createUserSchema = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
  password: z.string().min(8),
});

app.post('/api/users', zValidator('json', createUserSchema), async (c) => {
  const data = c.req.valid('json');

  // 检查邮箱是否已存在
  const existing = await userRepo.findByEmail(data.email);
  if (existing) {
    return c.json({ error: '邮箱已存在' }, 409);
  }

  const user = await userRepo.create({
    name: data.name,
    email: data.email,
    passwordHash: await Bun.password.hash(data.password),
  });

  return c.json({ id: user.id, name: user.name, email: user.email }, 201);
});

app.get('/api/users/:id', async (c) => {
  const id = parseInt(c.req.param('id'));
  const user = await userRepo.findWithOrders(id);

  if (!user) {
    return c.json({ error: '用户不存在' }, 404);
  }

  return c.json(user);
});

// ========== 订单 API ==========

const createOrderSchema = z.object({
  userId: z.number().int().positive(),
  items: z
    .array(
      z.object({
        productId: z.number().int().positive(),
        quantity: z.number().int().positive(),
      })
    )
    .min(1),
  shippingAddress: z.object({
    street: z.string(),
    city: z.string(),
    province: z.string(),
    zip: z.string(),
    country: z.string(),
  }),
  notes: z.string().optional(),
});

app.post('/api/orders', zValidator('json', createOrderSchema), async (c) => {
  try {
    const data = c.req.valid('json');
    const order = await orderService.createOrder(data);
    return c.json(order, 201);
  } catch (error: any) {
    return c.json({ error: error.message }, 400);
  }
});

// 启动服务器
export default {
  port: 3000,
  fetch: app.fetch,
};

console.log('Server running on http://localhost:3000');

第八章:生产环境部署最佳实践

8.1 环境变量管理

// src/config.ts
import { z } from 'zod';

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.coerce.number().default(3000),
  LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
});

export const config = envSchema.parse(process.env);

8.2 数据库健康检查

// src/health.ts
import { sql } from 'drizzle-orm';
import { db } from './db';

export async function checkDatabaseHealth() {
  try {
    const start = performance.now();
    await db.execute(sql`SELECT 1`);
    const latency = performance.now() - start;

    return {
      status: 'healthy',
      latency: `${latency.toFixed(2)}ms`,
      timestamp: new Date().toISOString(),
    };
  } catch (error) {
    return {
      status: 'unhealthy',
      error: error instanceof Error ? error.message : 'Unknown error',
      timestamp: new Date().toISOString(),
    };
  }
}

8.3 优雅迁移策略

# 开发环境:直接推送 Schema 变更
npx drizzle-kit push

# 生产环境:生成迁移文件 → 审核 → 执行
npx drizzle-kit generate
npx drizzle-kit migrate

# 回滚迁移(手动)
npx drizzle-kit migrate --to=0001_xxx

总结与展望

Drizzle ORM v1.0 的发布标志着 TypeScript ORM 进入了一个新的阶段。JIT Row Mapper 不只是一个性能优化——它代表了一种架构哲学:与其在运行时做通用化的抽象,不如在编译时或首次运行时生成最优化的专用代码

这个思路与 V8 的 TurboFan JIT 编译器、Svelte 的编译时优化、甚至 Bun 本身的 JIT 策略是一脉相承的。JavaScript/TypeScript 生态正在经历一场"编译时优先"的范式转移,Drizzle ORM 是这场转移在数据库层的最新代表。

对于开发者来说,Drizzle v1.0 意味着:

  1. TypeScript 服务端性能不再是妥协:ORM 开销接近零,性能瓶颈回到数据库本身
  2. Serverless/Edge 部署不再是障碍:7KB 的 Bundle 体积,冷启动几乎无感
  3. SQL 知识仍然有价值:Drizzle 不隐藏 SQL,而是让 SQL 更安全、更类型化
  4. 全栈 TypeScript 的最后一块拼图:从 Hono 到 Drizzle,从前端到数据库,一套语言搞定一切

如果你正在启动一个新的 TypeScript 后端项目,Drizzle ORM v1.0 值得认真考虑。如果你正在用 Prisma 或 TypeORM,遇到了性能或 Bundle 体积的瓶颈,Drizzle 可能是你的最佳迁移目标。

JavaScript 比 Go 快——这不是天方夜谭,而是 2026 年的现实。而 Drizzle ORM,正是让这个现实成为可能的关键拼图之一。


本文基于 Drizzle ORM v1.0.0-rc.1 版本撰写,API 可能在正式版中有微调。建议查阅 官方文档 获取最新信息。

复制全文 生成海报 Drizzle ORM TypeScript Bun Go JIT 数据库 SQL

推荐文章

CSS 实现金额数字滚动效果
2024-11-19 09:17:15 +0800 CST
使用 Go Embed
2024-11-19 02:54:20 +0800 CST
一键压缩图片代码
2024-11-19 00:41:25 +0800 CST
一键配置本地yum源
2024-11-18 14:45:15 +0800 CST
Python设计模式之工厂模式详解
2024-11-19 09:36:23 +0800 CST
程序员茄子在线接单