键设计与常用数据类型
选择 Redis 数据类型时,应根据要执行的操作选择,而不是把所有内容都序列化成字符串。
键命名
推荐使用冒号分层:
应用:模块:实体:标识
nloop:user:42
nloop:product:1001
nloop:article:88:views
好的键名应该:
- 能看出数据属于哪个应用和业务。
- 长度适中,不存入巨大文本。
- 不包含密码、身份证号等敏感信息。
- 对同一类数据保持固定格式。
Redis Cluster 中可以使用 Hash Tag 让相关键进入同一槽位:
cart:{user-42}:items
cart:{user-42}:summary
这属于 Cluster 进阶能力,单机阶段不必滥用。
String
String 可保存文本、数字或二进制内容,是最常用的数据类型。
await client.set("nloop:user:42:name", "Alice");
const name: string | null = await client.get("nloop:user:42:name");
await client.incr("nloop:article:88:views");
await client.incrBy("nloop:account:42:points", 10);
适合:缓存 JSON、验证码、计数器、幂等标记。
Hash
Hash 在一个 Redis 键下保存多个字段:
await client.hSet("nloop:user:42", {
name: "Alice",
email: "alice@example.com",
status: "active",
});
const user: Record<string, string> = await client.hGetAll("nloop:user:42");
const email: string | null = await client.hGet("nloop:user:42", "email");
适合经常单独读取或更新字段的对象。Hash 不会自动保留 TypeScript 类型,数字和布尔值仍需要显式转换。
List
List 是有顺序的字符串序列:
await client.lPush("nloop:tasks", "task-1");
await client.rPush("nloop:tasks", "task-2");
const task: string | null = await client.lPop("nloop:tasks");
适合简单队列或最近记录。需要可靠消费、确认、消费者组时,应考虑 Redis Stream 或专业消息队列。
Set
Set 保存不重复成员:
await client.sAdd("nloop:article:88:tags", ["nodejs", "redis"]);
const exists: number = await client.sIsMember(
"nloop:article:88:tags",
"redis",
);
const tags: string[] = await client.sMembers("nloop:article:88:tags");
适合标签、去重、共同好友和权限集合。
Sorted Set
Sorted Set 的每个成员都有分数,并按分数排序:
await client.zAdd("nloop:leaderboard", [
{ score: 120, value: "user:42" },
{ score: 95, value: "user:7" },
]);
const topUsers: string[] = await client.zRange(
"nloop:leaderboard",
0,
9,
{ REV: true },
);
适合排行榜、延迟队列和按时间排序的索引。
JSON 字符串还是 Hash
保存完整 JSON:
interface CachedUser {
id: number;
name: string;
}
const user: CachedUser = { id: 42, name: "Alice" };
await client.set("nloop:user-json:42", JSON.stringify(user));
读取时必须解析并校验:
const raw: string | null = await client.get("nloop:user-json:42");
if (raw !== null) {
const parsed: unknown = JSON.parse(raw);
// 外部数据在运行时仍应进行结构校验。
}
选择原则:
- 总是整体读写:JSON String 更简单。
- 经常单独修改字段:Hash 更合适。
- 需要嵌套 JSON 路径操作:了解 RedisJSON 模块,而不是自己发明复杂编码。
实用技巧
- 写代码前先在 Redis 命令文档中确认复杂度。
- 避免
SMEMBERS、LRANGE 0 -1读取不受控的大集合。 - 不要把几 MB 的巨大对象频繁存取,序列化和网络传输也会消耗时间。
- 对可能不存在的数据,在类型中保留
null/undefined,不要用非空断言掩盖问题。