核心工具链数据库与存储

数据库与存储

在 Bun 中选择 Bun.SQL、SQLite、Redis、S3 与 Drizzle,并正确管理连接和生产边界

最后更新于

最后核验:2026-08-02,Bun 1.3.14。易变的驱动能力与服务端版本要求应在升级时重新查询官方文档。

先选择数据边界

需求首选入口需要额外负责
PostgreSQL / MySQL 业务数据Bun.SQLSchema、迁移、索引、备份与连接池预算
本地文件数据库Bun.SQL SQLite 或 bun:sqliteWAL、并发写入、文件持久化与备份
缓存、计数器、短期状态RedisClientTTL、键空间、故障降级和连接关闭
对象与大文件S3ClientBucket 权限、生命周期、校验与 CDN
复杂类型查询和迁移Drizzle + Bun.SQLORM 版本兼容、迁移发布流程

不要因为 Bun 内置了客户端,就把数据库迁移、数据模型和云服务运维当成运行时已经解决的问题。

Bun.SQL 最小安全查询

src/users.ts
import { SQL } from 'bun';

const databaseUrl = Bun.env.DATABASE_URL;
if (!databaseUrl) throw new Error('DATABASE_URL is required');

const database = new SQL(databaseUrl, {
  max: 10,
  connectionTimeout: 10,
  idleTimeout: 30,
});

export async function findActiveUser(email: string) {
  const rows = await database`
    SELECT id, email
    FROM users
    WHERE email = ${email} AND active = ${true}
    LIMIT 1
  `;

  return rows[0] ?? null;
}

export function closeDatabase() {
  return database.close();
}

插值参数会走参数化查询;表名、列名和排序方向不能把未验证的用户输入直接拼进 SQL。连接池的 max单进程上限,副本数增加时要计算数据库总连接数。

统一 API 不代表数据库行为相同

MySQL 没有 PostgreSQL 的原生数组和相同的 RETURNING 行为;SQLite 的并发和类型系统也不同。集成测试必须使用生产目标数据库。

当前 Bun.SQL 的 PostgreSQL 客户端仍未实现部分能力,包括 COPYLISTENNOTIFY、GSSAPI 和部分 PostGIS 类型。依赖这些能力时保留成熟驱动,不要为了统一 API 强行迁移。

Drizzle 适合承担什么

Drizzle 可在 Bun.SQL 上提供 Schema、查询构建、关系和迁移工具:

src/database/index.ts
import { SQL } from 'bun';
import { drizzle } from 'drizzle-orm/bun-sql';

const connectionString = Bun.env.DATABASE_URL;
if (!connectionString) throw new Error('DATABASE_URL is required');

const client = new SQL(connectionString);
export const database = drizzle({ client });

Drizzle 的 Bun.SQL 指南在本次核验时仍展示 RC 包安装命令。采用前应同时核对 Drizzle、Drizzle Kit、Bun 和目标数据库版本;迁移应在单独发布步骤执行,而不是让每个服务副本启动时自动执行。

Redis 生命周期

Bun 原生 Redis 客户端当前支持 Redis 7.2 及以上:

import { RedisClient } from 'bun';

const redisUrl = Bun.env.REDIS_URL;
if (!redisUrl) throw new Error('REDIS_URL is required');

const cache = new RedisClient(redisUrl);

await cache.set('health:last-ok', new Date().toISOString());
const lastOk = await cache.get('health:last-ok');

cache.close();

缓存不可用时是失败、降级还是绕过,必须由业务决定。不要把没有持久化契约的缓存当作唯一事实源,也不要在键或日志中写入密钥和完整提示词。

S3 兼容对象存储

import { S3Client } from 'bun';

const storage = new S3Client({
  endpoint: Bun.env.S3_ENDPOINT,
  accessKeyId: Bun.env.S3_ACCESS_KEY_ID,
  secretAccessKey: Bun.env.S3_SECRET_ACCESS_KEY,
  bucket: Bun.env.S3_BUCKET,
});

await storage.file('reports/latest.json').write(JSON.stringify({ ok: true }));

S3Client 可用于 AWS S3、Cloudflare R2、MinIO 等兼容服务,但签名、区域、Endpoint、公开访问和生命周期策略仍以目标服务文档为准。上传前限制大小和类型;下载敏感对象时优先使用短时授权,不要公开 Bucket。

生产完成标准

  • 密钥只从运行环境或 Secret Manager 注入;
  • 查询参数化,动态标识符有 allowlist;
  • 连接池预算乘以最大副本数后不超过数据库限制;
  • 数据库迁移可审查、可回滚,并且只执行一次;
  • 集成测试使用目标数据库版本,不用 SQLite 代替 PostgreSQL 行为测试;
  • SIGTERM 时关闭 SQL、Redis 和其他长连接;
  • 备份、恢复和对象生命周期经过演练。

官方参考:Bun.SQLRedisS3Drizzle Bun.SQL