先看一个真实场景:100 万条 Redis 键需要处理,CPU 密集 + I/O 密集并存,怎么破?用 Bun.redis + Worker,4 核机器跑出 4 倍加速,单线程能撑 4 万 QPS。
⚠️ 环境要求 :Bun ≥ 1.3.14,Redis ≥ 6.0 (Bun.redis 默认用 RESP3 协议握手,需要 Redis 6+ 的
HELLO命令支持)。Redis 5 及以下会报ERR unknown command 'HELLO'。
100 万条键的"压榨"实验
假设你的 Redis 里有 100 万个用户画像(Hash 结构),现在要做一次全量数据加工:
bash
// 每个 user 是一个 Hash,有 name、age、tags 等几十个字段
// 任务:取出 → 解析 → 加密敏感字段 → 写回
朴素版本(单线程串行)------ 别想跑完
bash
const redis = new Bun.RedisClient("redis://localhost:6379");
const keys = await redis.keys("user:*"); // 100 万
for (const key of keys) {
const data = await redis.hgetall(key); // I/O
const encrypted = encrypt(JSON.stringify(data)); // CPU
await redis.hset(key, { data: encrypted }); // I/O
}
// 预估耗时:3-5 小时
// 事件循环 100% 阻塞,HTTP 服务直接卡死
进阶版本:自动 Pipeline(不用 Worker)------ I/O 起来了,CPU 还是瓶颈
bash
// 先把所有 key 一次性取出来(自动 pipeline,1 次 RTT)
const pipeline = [];
for (let i = 0; i < 1000; i++) pipeline.push(redis.hgetall(`user:${i}`));
const datas = await Promise.all(pipeline); // 1ms 拿到
// 但 encrypt() 在主线程跑
for (const data of datas) {
encrypt(JSON.stringify(data)); // 卡住事件循环
}
// 预估耗时:40-60 分钟(I/O 快,CPU 慢)
// 加密 10 万次时,HTTP P99 飙到 5 秒
终极版本:Worker + Bun.redis ------ 4 核满载
bash
// main.ts
// ⚠️ Worker 是 Bun 的全局对象,无需 import
const redis = new Bun.RedisClient("redis://localhost:6379");
const keys = await redis.keys("user:*");
const chunks = chunkArray(keys, 4); // 分 4 份
const workers = chunks.map(() => new Worker(new URL("./encrypt-worker.ts", import.meta.url)));
const results = await Promise.all(
workers.map((w, i) => {
w.postMessage(chunks[i]);
return new Promise((resolve) => (w.onmessage = (e) => resolve(e.data)));
})
);
console.log(`处理完成:${results.reduce((a, b) => a + b, 0)} 条`);
// 预估耗时:10-15 分钟(4 倍加速)
// 事件循环全程畅通,HTTP 服务不受影响
bash
// encrypt-worker.ts
// ✅ Bun.RedisClient 是全局;也可以 import { RedisClient } from "bun"
declare var self: Worker;
self.onmessage = async (e) => {
const redis = new Bun.RedisClient("redis://localhost:6379");
let count = 0;
// 在独立线程里跑 CPU 密集逻辑,不阻塞主事件循环
for (const key of e.data) {
const data = await redis.hgetall(key);
const encrypted = encrypt(JSON.stringify(data));
await redis.send("HSET", [key, "data", encrypted]); // 见下文 Streams 同款 send
count++;
}
self.postMessage(count);
};
| 版本 | 耗时 | CPU 利用率 | 事件循环阻塞 | | :-- | :-- | :-- | :-- | | 朴素串行 | 3-5 小时 | 25%(单核) | 完全卡死 | | 自动 Pipeline(单线程) | 40-60 分钟 | 25%(单核) | 加密时卡死 | | Worker + Bun.redis(4 核) | 10-15 分钟 | 100%(4 核满载) | 完全畅通 |
这就是 Bun.redis 真正的强项:不是"某一项特别强",而是"能让你把多线程 + 高并发 + 自动 Pipeline 这三件事用最少的代码做出来"。
下面我们一项一项拆开看。
一、强项全景图
| 维度 | Bun.redis | ioredis / node-redis | | :-- | :-- | :-- | | 安装 | ✅ 零依赖,内置 | ❌ 需 npm i,版本依赖地狱 | | 性能 | ⭐⭐⭐⭐⭐ Zig/Rust 内核 | ⭐⭐⭐⭐ JS + libuv | | Pipeline | ✅ 自动启用 | ❌ 需手动 .pipeline() | | TypeScript | ✅ 原生类型 | ⚠️ 需 @types/ioredis | | API 设计 | 类 fetch / Promise 风格 | 链式 .then() | | 冷启动 | 几乎零开销 | 多 2-5ms 解析依赖 | | 集群 / Pub-Sub / Stream | ✅ 全支持 | ✅ 支持但需额外配置 | | 多线程(Worker) | ✅ postMessage 快 400 倍 | ⚠️ 几百微秒序列化 |
一句话总结:你写的是 Bun 代码,Bun.redis 就像 fetch、Bun.file 一样自然,不用想"装哪个包"。
二、强项一:零依赖,原生集成
Node.js 生态里,Redis 客户端是"重资产":
bash
# Node.js
npm install ioredis # 还要装 @types/ioredis
# 版本冲突?TypeScript 报错?锁文件爆炸?
Bun 直接给你:
bash
// Bun - 零依赖
const redis = new Bun.RedisClient("redis://localhost:6379");
await redis.set("key", "value");
没有 node_modules,没有版本冲突,没有 Cannot find module 'ioredis'。这在 Serverless、边缘计算、Docker 镜像里收益巨大------镜像小、冷启动快、依赖干净。
三、强项二:Zig/Rust 内核,性能碾压
Bun.redis 用 Zig/Rust 直接实现 RESP 协议解析,绕过 libuv,直接系统调用。
官方基准(GET/SET 场景)
| 客户端 | ops/sec | 延迟 P99 | 内存占用 | | :-- | :-- | :-- | :-- | | Bun.redis | ~280,000 | 0.3ms | 基准 | | ioredis | ~190,000 | 0.6ms | +40% | | node-redis | ~170,000 | 0.8ms | +50% |
关键点:
-
• 协议解析在原生代码里跑,不消耗 JS 线程时间
-
• TCP socket 用
epoll/kqueue/io_uring直接管理 -
• 零拷贝 buffer 传递,JS 端拿到的是 TypedArray,不复制
压测代码
bash
import { Bench } from "tinybench";
const bench = new Bench({ time: 5000 });
const redis = new Bun.RedisClient("redis://localhost:6379");
bench
.add("Bun.redis SET", async () => {
await redis.set(`k:${Math.random()}`, "value");
})
.add("Bun.redis GET", async () => {
await redis.get("k:0.5");
});
await bench.run();
console.table(bench.table());
四、强项三:自动 Pipeline,最强隐藏特性
这是 Bun.redis 最被低估 的强项------它默认就是 pipeline ,不用 .pipeline() 也不用 .multi()。
1000 个请求对比
ioredis(需要手动 pipeline):
bash
const redis = new Redis();
const pipeline = redis.pipeline();
for (let i = 0; i < 1000; i++) {
pipeline.get(`key:${i}`);
}
const results = await pipeline.exec();
Bun.redis(自动 pipeline):
bash
const redis = new Bun.RedisClient("redis://localhost:6379");
const promises = [];
for (let i = 0; i < 1000; i++) {
promises.push(redis.get(`key:${i}`));
}
const results = await Promise.all(promises);
底层发生了什么?
性能差距:
| 模式 | 网络往返次数 | 耗时 | | :-- | :-- | :-- | | 串行 await(无 pipeline) | 1000 次 | ~3000ms | | ioredis 手动 pipeline | 1 次 | ~3ms | | Bun.redis 自动 pipeline | 1 次 | ~2ms |
1000 倍加速,就这么来的。
五、强项四:TypeScript 原生一等公民
不用 @types/xxx,不用 declare module,类型推断是顶级的:
bash
const redis = new Bun.RedisClient("redis://localhost:6379");
// 完整类型提示
// 传对象会直接 TS 报错 + 运行时 TypeError,不会偷偷 toString
await redis.set("user:1", "Alice"); // 必须是 string | number | Buffer
const result = await redis.get("user:1");
// result: string | null ← 明确告诉你可能为 null
// 批量 mget:注意是变长参数(不是数组)
const users = await redis.mget("user:1", "user:2", "user:3");
// users: (string | null)[]
// 哈希操作(hset 支持多种形式:单字段、变长对、对象)
await redis.hset("user:1", { name: "Alice", age: "30" });
// 等价于:
// await redis.hmset("user:1", ["name", "Alice", "age", "30"]);
// await redis.hset("user:1", "name", "Alice", "age", "30");
const user = await redis.hgetall("user:1");
// user: { name: string, age: string, ... }(实际是带 null 原型的对象)
链式 API 也有完整类型:
bash
// ZADD 返回添加的元素数量
const added: number = await redis.zadd("scores", 100, "alice", 200, "bob");
// ZRANGEBYSCORE 返回数组
const top: string[] = await redis.zrangebyscore("scores", 0, 1000);
六、强项五:现代 API 设计
Bun.redis 的 API 借鉴了 fetch / Promise 的现代风格,比 ioredis 的链式调用清爽得多。
连接管理
bash
// 简单连接
const redis = new Bun.RedisClient("redis://localhost:6379");
// URL 携带认证
const redis2 = new Bun.RedisClient("redis://:password@host:6379/0");
// TLS + 集群
const redis3 = new Bun.RedisClient("rediss://cluster.example.com:6380", {
tls: { rejectUnauthorized: true },
});
// 带连接选项(注意:没有 reconnect 字段,是 autoReconnect + maxRetries)
const redis4 = new Bun.RedisClient("redis://localhost:6379", {
connectionTimeout: 5000, // 连接超时(毫秒)
idleTimeout: 30000, // 空闲超时
autoReconnect: true, // 自动重连(默认 true)
maxRetries: 10, // 最大重试次数
enableAutoPipelining: true, // 自动 Pipeline(默认 true)
});
Pub/Sub(原生支持)
bash
const sub = new Bun.RedisClient("redis://localhost:6379");
await sub.subscribe("news", (message, channel) => {
console.log(`[${channel}] ${message}`);
});
// 发布
const pub = new Bun.RedisClient("redis://localhost:6379");
await pub.publish("news", "Hello world");
Streams(事件流)
bash
const redis = new Bun.RedisClient("redis://localhost:6379");
// ⚠️ Bun.redis 没有 xadd/xread 便捷方法,要用 send 走原始协议
// 生产者
const id = await redis.send("XADD", ["events", "*", "type", "click", "user", "alice"]);
// 消费者
const stream = await redis.send("XREAD", ["COUNT", "10", "STREAMS", "events", "0"]);
console.log(stream);
事务 / Lua 脚本
bash
// ⚠️ Bun.redis 没有 multi()/eval() 便捷方法,要用 send
// MULTI/EXEC
await redis.send("MULTI", []);
await redis.send("SET", ["a", "1"]);
await redis.send("INCR", ["b"]);
const results = await redis.send("EXEC", []); // ["OK", 1]
console.log(results);
// EVAL
const result = await redis.send(
"EVAL",
["return redis.call('GET', KEYS[1])", "1", "mykey"]
);
七、强项六:自动重连 + 集群感知
自动重连(开箱即用)
bash
const redis = new Bun.RedisClient("redis://localhost:6379", {
// ⚠️ 选项名是 autoReconnect / maxRetries,不是 reconnect
autoReconnect: true, // 断线自动重连,无需自己写重试逻辑
maxRetries: 10, // 最多重试 10 次(默认)
// 内部用 50ms 起步、每次翻倍的指数退避,封顶 2 秒
});
// 你只管 await,断网恢复后自动继续
const value = await redis.get("key");
// 还可以监听连接事件
redis.onconnect = () => console.log("connected");
redis.onclose = (err) => console.log("disconnected:", err);
console.log(redis.connected); // boolean
console.log(redis.bufferedAmount); // 当前缓冲字节数
集群模式
bash
// 多个节点自动分片(用一个客户端连多个节点)
const cluster = new Bun.RedisClient([
"redis://node1:6379",
"redis://node2:6379",
"redis://node3:6379",
]);
// 内部自动计算 slot、路由请求、处理节点故障
await cluster.set("user:1", "Alice");
const user = await cluster.get("user:1");
八、强项七:Worker 多线程,比 Node.js 快 400 倍
如果你真需要"计算 + Redis"两不误的多线程方案,Bun 的 Worker 是"最快的接缝"。
基础用法(回开放头那个例子)
bash
// main.ts
const worker = new Worker("./worker.ts");
worker.postMessage({ keys: ["a", "b", "c", "d"] });
worker.onmessage = (e) => console.log("处理完成:", e.data);
bash
// worker.ts
declare var self: Worker;
self.onmessage = async (e) => {
const redis = new Bun.RedisClient("redis://localhost:6379");
const results = [];
// 在 Worker 线程里跑 CPU 密集逻辑
for (const key of e.data.keys) {
const value = await redis.get(key);
results.push(transform(value)); // 假设 transform 很耗时
}
self.postMessage(results);
};
性能差距(Bun 1.3.14 vs Node.js 24.6.0)
postMessage 传 3MB 字符串:
| 运行时 | 耗时 | 内存峰值 | | :-- | :-- | :-- | | Bun 1.3.14 | 593 ns | 极少 | | Bun 1.2.21 | 326,290 ns | 多 | | Node.js 24.6.0 | 242,110 ns | 多 22 倍 |
关键原理 :JavaScriptCore 引擎里的字符串是线程安全的引用计数对象 ,Bun 干脆零拷贝传指针,根本不序列化。Node.js 还在用结构化克隆算法完整复制。
实战:通用 Worker Pool
bash
type Task = { id: number; key: string };
type Result = { id: number; value: string | null };
export class RedisWorkerPool {
private workers: Worker[] = [];
private queue: Task[] = [];
private busy = new Set<Worker>();
private results: Result[] = [];
private expected = 0;
private onAllDone?: () => void;
constructor(workerPath: string, size = 4) {
for (let i = 0; i < size; i++) {
const w = new Worker(workerPath);
w.onmessage = (e) => this.handleResult(w, e.data);
this.workers.push(w);
}
}
process(tasks: Task[]): Promise<Result[]> {
this.expected = tasks.length;
this.results = [];
this.queue.push(...tasks);
this.dispatch();
return new Promise((resolve) => {
this.onAllDone = () => resolve(this.results);
});
}
private dispatch() {
while (this.queue.length > 0) {
const idle = this.workers.find((w) => !this.busy.has(w));
if (!idle) break;
const task = this.queue.shift()!;
this.busy.add(idle);
idle.postMessage(task);
}
}
private handleResult(worker: Worker, result: Result) {
this.busy.delete(worker);
this.results.push(result);
if (this.results.length >= this.expected) {
// 所有任务完成,触发外层 resolve
this.onAllDone?.();
} else {
this.dispatch();
}
}
terminate() {
this.workers.forEach((w) => w.terminate());
}
}
说明 :文章前一版
process()里的Promise永远不会 resolve------handleResult走到 "所有任务完成" 分支时只留了个空注释。这里加了expected计数 +onAllDone回调,让process()能真正返回结果。
适用场景:
| 场景 | 是否需要 Worker | | :-- | :-- | | 普通的 GET / SET / HGETALL | ❌ 不需要,async/await 够了 | | 批量 Pipeline 上万个 key | ❌ 不需要,Bun.redis 自己处理 | | 拿到数据后做复杂计算(JSON 解析、加密、聚合) | ✅ 用 Worker 避免阻塞 | | 多个独立任务并行处理(比如同时跑 5 个报表) | ✅ 一个任务一个 Worker | | 大数据量序列化 / 反序列化 | ✅ Worker 隔离 |
九、对比表:为什么值得切换?
| 维度 | Bun.redis | ioredis | node-redis | | :-- | :-- | :-- | :-- | | 安装体积 | 0 KB | ~150 KB | ~200 KB | | 冷启动开销 | < 0.1ms | 2-3ms | 3-5ms | | 吞吐量(pipeline) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | | 内存占用 | 极低 | 中 | 中 | | TypeScript | 原生 | 需 types | 需 types | | 自动 pipeline | ✅ | ❌ | ❌ | | Cluster 复杂度 | 低 | 中 | 中 | | postMessage(3MB 字符串) | 593 ns | --- | 242,110 ns | | 生态成熟度 | 新(1.0+) | 老牌(10 年) | 老牌(10 年) | | 文档 / Stack Overflow | 较少 | 极丰富 | 极丰富 |
迁移成本:
-
• API 90% 相似,常用命令(GET/SET/HSET/ZADD/EXPIRE)名字都一样
-
• 最大的差异:ioredis 的链式 → Bun.redis 的 async/await
-
• Redis Cluster、Pub/Sub、Stream 都能用
十、实战:3 行代码搭一个 Redis 缓存层
bash
// cache.ts
const redis = new Bun.RedisClient("redis://localhost:6379");
export async function cache<T>(key: string, ttl: number, fetcher: () => Promise<T>): Promise<T> {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const fresh = await fetcher();
// ⚠️ Bun.redis 的 set() 不支持选项对象,要走 send 设过期
await redis.send("SET", [key, JSON.stringify(fresh), "EX", String(ttl)]);
return fresh;
}
// 使用
const user = await cache("user:1", 60, () => db.user.find(1));
没有 ioredis,没有 @types/ioredis,没有版本警告------就这么简单。
十一、注意事项:Bun.redis 还不能用的场景
诚实地讲,Bun.redis 不适合所有人:
| 场景 | 建议 | | :-- | :-- | | 需要在 Node.js 里跑 | ❌ 用 ioredis(API 完全不同) | | 用了 ioredis 的高级插件(哨兵、Cluster 复杂配置) | ⚠️ 评估迁移成本 | | 已有大量 ioredis 代码 | ⚠️ 一次性迁移不划算 | | Bun 版本 < 1.0 | ❌ Redis 客户端还不稳定 | | 团队不熟悉 Bun | ⚠️ 学习曲线 |
十二、总结
Bun.redis 真正的强项,归纳成 6 个词:
一句话: Bun.redis 不是一个"Redis 客户端库",而是 Bun 运行时的"内置基础设施" ------就像 fetch、Bun.file、Bun.serve 一样,写 Bun 代码就该用它。
如果你想"榨干 CPU + Redis 的最后一点性能",那就把它丢进 Worker 吧------4 核机器,4 倍加速,仅此而已。
📖 参考资料
-
• Bun 官方文档 - Redis Client
-
• Bun 官方文档 - Workers
-
• Bun 1.3.14 postMessage 性能优化
-
• Bun 性能基准对比
-
• RESP 协议规范