Bun 的 Redis 客户端支持 Redis 服务器版本 7.2 及以上。
Bun 提供用于操作 Redis 数据库的原生绑定,采用现代化的 Promise API。该接口设计简洁高效,具备内置连接管理、全类型化响应和 TLS 支持。

redis.ts
import { redis } from "bun";
// 设置键值
await redis.set("greeting", "Hello from Bun!");
// 获取键值
const greeting = await redis.get("greeting");
console.log(greeting); // "Hello from Bun!"
// 递增计数器
await redis.set("counter", 0);
await redis.incr("counter");
// 检查键是否存在
const exists = await redis.exists("greeting");
// 删除键
await redis.del("greeting");
使用 Redis 客户端,首先需要创建一个连接:

redis.ts
import { redis, RedisClient } from "bun";
// 使用默认客户端(连接信息从环境变量读取)
// 默认使用 process.env.REDIS_URL
await redis.set("hello", "world");
const result = await redis.get("hello");
// 创建自定义客户端
const client = new RedisClient("redis://username:password@localhost:6379");
await client.set("counter", "0");
await client.incr("counter");
默认情况下,客户端按优先级从以下环境变量读取连接信息:
REDIS_URL
VALKEY_URL
- 若未设置,默认使用
"redis://localhost:6379"
连接生命周期
Redis 客户端会自动在后台管理连接:

redis.ts
// 未执行命令前不会连接
const client = new RedisClient();
// 第一次命令触发连接建立
await client.set("key", "value");
// 连接保持开启,供后续命令使用
await client.get("key");
// 使用结束后显式关闭连接
client.close();
你也可以手动控制连接生命周期:

redis.ts
const client = new RedisClient();
// 显式连接
await client.connect();
// 执行命令
await client.set("key", "value");
// 完成后断开连接
client.close();
基本操作
字符串操作

redis.ts
// 设置键值
await redis.set("user:1:name", "Alice");
// 获取键值
const name = await redis.get("user:1:name");
// 以 Uint8Array 形式获取键值
const buffer = await redis.getBuffer("user:1:name");
// 删除键
await redis.del("user:1:name");
// 检查键是否存在
const exists = await redis.exists("user:1:name");
// 设置过期时间(秒)
await redis.set("session:123", "active");
await redis.expire("session:123", 3600); // 一小时后过期
// 获取剩余生存时间(秒)
const ttl = await redis.ttl("session:123");
数值操作

redis.ts
// 设置初始值
await redis.set("counter", "0");
// 自增 1
await redis.incr("counter");
// 自减 1
await redis.decr("counter");
哈希操作

redis.ts
// 设置哈希多个字段
await redis.hmset("user:123", ["name", "Alice", "email", "[email protected]", "active", "true"]);
// 获取哈希多个字段
const userFields = await redis.hmget("user:123", ["name", "email"]);
console.log(userFields); // ["Alice", "[email protected]"]
// 获取单个哈希字段(不存在返回 null)
const userName = await redis.hget("user:123", "name");
console.log(userName); // "Alice"
// 哈希字段数值递增
await redis.hincrby("user:123", "visits", 1);
// 哈希字段浮点数递增
await redis.hincrbyfloat("user:123", "score", 1.5);
集合操作

redis.ts
// 添加成员到集合
await redis.sadd("tags", "javascript");
// 从集合移除成员
await redis.srem("tags", "javascript");
// 检查成员是否在集合中
const isMember = await redis.sismember("tags", "javascript");
// 获取集合所有成员
const allTags = await redis.smembers("tags");
// 获取随机成员
const randomTag = await redis.srandmember("tags");
// 弹出(移除并返回)随机成员
const poppedTag = await redis.spop("tags");
发布/订阅(Pub/Sub)
Bun 提供了对Redis 发布/订阅协议的原生支持。Bun 1.2.23 新增
Redis 发布/订阅功能目前处于实验阶段。虽然我们期望其稳定,但目前正在积极收集反馈和改进点。
基本用法
要开始发布消息,可以在 publisher.ts 中设置发布者:

publisher.ts
import { RedisClient } from "bun";
const writer = new RedisClient("redis://localhost:6739");
await writer.connect();
writer.publish("general", "Hello everyone!");
writer.close();
在另一个文件 subscriber.ts 中创建订阅者:

subscriber.ts
import { RedisClient } from "bun";
const listener = new RedisClient("redis://localhost:6739");
await listener.connect();
await listener.subscribe("general", (message, channel) => {
console.log(`Received: ${message}`);
});
在一个终端运行订阅者:
另一个终端运行发布者:
订阅模式会占用 RedisClient 的连接。具有订阅的客户端只能调用 RedisClient.prototype.subscribe()。也就是说,应用中如果需要同时向 Redis 发送消息,需要使用单独的连接,可通过 .duplicate() 获得:
redis.ts
import { RedisClient } from "bun";
const redis = new RedisClient("redis://localhost:6379");
await redis.connect();
const subscriber = await redis.duplicate();
await subscriber.subscribe("foo", () => {});
await redis.set("bar", "baz");
发布消息
使用 publish() 方法发布消息:

redis.ts
await client.publish(channelName, message);
订阅频道
Bun 的 RedisClient 通过 .subscribe() 方法订阅频道:

redis.ts
await client.subscribe(channel, (message, channel) => {});
可通过 .unsubscribe() 方法取消订阅:

redis.ts
await client.unsubscribe(); // 取消所有频道订阅
await client.unsubscribe(channel); // 取消指定频道订阅
await client.unsubscribe(channel, listener); // 取消指定监听器订阅
高级用法
命令执行与流水线
客户端默认自动进行流水线处理,通过批量发送多个命令并在响应到达时依序处理,提升性能:

redis.ts
// 命令默认自动流水线处理
const [infoResult, listResult] = await Promise.all([redis.get("user:1:name"), redis.get("user:2:email")]);
可通过设置 enableAutoPipelining 选项关闭自动流水线:

redis.ts
const client = new RedisClient("redis://localhost:6379", {
enableAutoPipelining: false,
});
原生命令执行
当需要执行没有封装为便捷方法的命令时,可以使用 send 方法:

redis.ts
// 执行任意 Redis 命令
const info = await redis.send("INFO", []);
// 向列表 LPUSH
await redis.send("LPUSH", ["mylist", "value1", "value2"]);
// 获取列表范围
const list = await redis.send("LRANGE", ["mylist", "0", "-1"]);
send 方法第一个参数是命令名称,第二个参数是字符串数组形式的命令参数。
连接事件
可以注册连接事件处理函数:

redis.ts
const client = new RedisClient();
// 成功连接 Redis 服务器时调用
client.onconnect = () => {
console.log("Connected to Redis server");
};
// 与 Redis 服务器断开连接时调用
client.onclose = error => {
console.error("Disconnected from Redis server:", error);
};
// 手动连接与断开
await client.connect();
client.close();
连接状态及监控

redis.ts
// 检查连接状态
console.log(client.connected); // 布尔值,表示连接是否建立
// 查看缓冲区数据大小(字节)
console.log(client.bufferedAmount);
类型转换
Redis 客户端自动处理响应的类型转换:
- 整数类型返回 JavaScript 数字
- 大块字符串返回 JavaScript 字符串
- 简单字符串返回 JavaScript 字符串
- 空字符串返回
null
- 数组返回 JavaScript 数组
- 错误响应抛出对应错误对象,包含错误代码
- 布尔类型(RESP3)返回 JavaScript 布尔值
- 映射类型(RESP3)返回 JavaScript 对象
- 集合类型(RESP3)返回 JavaScript 数组
特殊命令特别处理:
EXISTS 返回布尔值(1 转 true,0 转 false)
SISMEMBER 返回布尔值(1 转 true,0 转 false)
以下命令会禁用自动流水线:
AUTH
INFO
QUIT
EXEC
MULTI
WATCH
SCRIPT
SELECT
CLUSTER
DISCARD
UNWATCH
PIPELINE
SUBSCRIBE
UNSUBSCRIBE
UNPSUBSCRIBE
连接选项
创建客户端时,可传入多种选项配置连接:

redis.ts
const client = new RedisClient("redis://localhost:6379", {
// 连接超时(毫秒),默认 10000
connectionTimeout: 5000,
// 空闲超时(毫秒),默认 0(不超时)
idleTimeout: 30000,
// 断线后是否自动重连,默认 true
autoReconnect: true,
// 最大重连次数,默认 10 次
maxRetries: 10,
// 断线时是否队列命令,默认 true
enableOfflineQueue: true,
// 是否自动流水线命令,默认 true
enableAutoPipelining: true,
// TLS 配置(默认 false)
tls: true,
// 或者提供自定义 TLS 配置:
// tls: {
// rejectUnauthorized: true,
// ca: "path/to/ca.pem",
// cert: "path/to/cert.pem",
// key: "path/to/key.pem",
// }
});
重连行为
连接断开时,客户端会自动尝试使用指数退避算法重连:
- 初始延迟较短(50 毫秒),之后每次尝试倍增延迟
- 重连延迟封顶为 2000 毫秒(2 秒)
- 最多尝试重连
maxRetries 次(默认 10 次)
- 断线期间执行的命令:
- 若
enableOfflineQueue 为 true,会加入队列等待重连后执行(默认)
- 若为 false,则立即拒绝执行
支持的 URL 格式
Redis 客户端支持多种连接 URL 格式:

redis.ts
// 标准 Redis URL
new RedisClient("redis://localhost:6379");
new RedisClient("redis://localhost:6379");
// 带认证信息
new RedisClient("redis://username:password@localhost:6379");
// 带数据库编号
new RedisClient("redis://localhost:6379/0");
// TLS 连接
new RedisClient("rediss://localhost:6379");
new RedisClient("rediss://localhost:6379");
new RedisClient("redis+tls://localhost:6379");
new RedisClient("redis+tls://localhost:6379");
// Unix 套接字连接
new RedisClient("redis+unix:///path/to/socket");
new RedisClient("redis+unix:///path/to/socket");
// Unix 套接字上的 TLS
new RedisClient("redis+tls+unix:///path/to/socket");
new RedisClient("redis+tls+unix:///path/to/socket");
错误处理
Redis 客户端会抛出带类型的错误,用于不同场景的异常:

redis.ts
try {
await redis.get("non-existent-key");
} catch (error) {
if (error.code === "ERR_REDIS_CONNECTION_CLOSED") {
console.error("与 Redis 服务器的连接已关闭");
} else if (error.code === "ERR_REDIS_AUTHENTICATION_FAILED") {
console.error("认证失败");
} else {
console.error("意外错误:", error);
}
}
常见错误码:
ERR_REDIS_CONNECTION_CLOSED - 服务器连接关闭
ERR_REDIS_AUTHENTICATION_FAILED - 认证失败
ERR_REDIS_INVALID_RESPONSE - 收到服务器无效响应
示例用例

redis.ts
async function getUserWithCache(userId) {
const cacheKey = `user:${userId}`;
// 尝试先从缓存获取
const cachedUser = await redis.get(cacheKey);
if (cachedUser) {
return JSON.parse(cachedUser);
}
// 缓存未命中,从数据库获取
const user = await database.getUser(userId);
// 缓存 1 小时
await redis.set(cacheKey, JSON.stringify(user));
await redis.expire(cacheKey, 3600);
return user;
}

redis.ts
async function rateLimit(ip, limit = 100, windowSecs = 3600) {
const key = `ratelimit:${ip}`;
// 计数自增
const count = await redis.incr(key);
// 第一次请求设置过期时间
if (count === 1) {
await redis.expire(key, windowSecs);
}
// 检测是否超出限制
return {
limited: count > limit,
remaining: Math.max(0, limit - count),
};
}
会话存储

redis.ts
async function createSession(userId, data) {
const sessionId = crypto.randomUUID();
const key = `session:${sessionId}`;
// 存储会话,设置过期时间
await redis.hmset(key, ["userId", userId.toString(), "created", Date.now().toString(), "data", JSON.stringify(data)]);
await redis.expire(key, 86400); // 24小时
return sessionId;
}
async function getSession(sessionId) {
const key = `session:${sessionId}`;
// 获取会话数据
const exists = await redis.exists(key);
if (!exists) return null;
const [userId, created, data] = await redis.hmget(key, ["userId", "created", "data"]);
return {
userId: Number(userId),
created: Number(created),
data: JSON.parse(data),
};
}
实现说明
Bun 的 Redis 客户端使用 Zig 实现,基于 Redis 序列化协议(RESP3)。它高效管理连接,具备自动断线重连机制,采用指数退避策略。
客户端支持命令流水线,即发送多个命令后无需等待先前命令响应,可显著提升多条命令顺序执行时的性能。
限制及未来规划
当前 Redis 客户端存在的一些限制,未来版本有计划改进:
- 事务(MULTI/EXEC)目前需通过原生命令执行
不支持的功能:
- Redis Sentinel
- Redis Cluster