数据库与存储
在 Bun 中选择 Bun.SQL、SQLite、Redis、S3 与 Drizzle,并正确管理连接和生产边界
最后更新于
最后核验:2026-08-02,Bun 1.3.14。易变的驱动能力与服务端版本要求应在升级时重新查询官方文档。
先选择数据边界
| 需求 | 首选入口 | 需要额外负责 |
|---|---|---|
| PostgreSQL / MySQL 业务数据 | Bun.SQL | Schema、迁移、索引、备份与连接池预算 |
| 本地文件数据库 | Bun.SQL SQLite 或 bun:sqlite | WAL、并发写入、文件持久化与备份 |
| 缓存、计数器、短期状态 | RedisClient | TTL、键空间、故障降级和连接关闭 |
| 对象与大文件 | S3Client | Bucket 权限、生命周期、校验与 CDN |
| 复杂类型查询和迁移 | Drizzle + Bun.SQL | ORM 版本兼容、迁移发布流程 |
不要因为 Bun 内置了客户端,就把数据库迁移、数据模型和云服务运维当成运行时已经解决的问题。
Bun.SQL 最小安全查询
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 客户端仍未实现部分能力,包括 COPY、LISTEN、NOTIFY、GSSAPI 和部分 PostGIS 类型。依赖这些能力时保留成熟驱动,不要为了统一 API 强行迁移。
Drizzle 适合承担什么
Drizzle 可在 Bun.SQL 上提供 Schema、查询构建、关系和迁移工具:
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.SQL、Redis、S3、Drizzle Bun.SQL。