为什么需要 Redis

Redis 是什么

Redis 是一个以内存为主要工作介质的数据存储系统。它不仅能保存字符串,还提供 Hash、List、Set、Sorted Set、Stream 等数据结构,以及过期、事务、发布订阅、Lua 脚本等能力。

内存访问通常比磁盘数据库更快,因此 Redis 很适合处理“访问频繁、允许用空间换时间、需要快速读写”的数据。

为什么不能只使用 MySQL

MySQL 擅长持久保存业务事实、处理关系、约束和复杂查询。例如订单、用户、付款记录应该以数据库为最终依据。

但如果每个 HTTP 请求都重复执行相同查询,会产生额外延迟和数据库压力:

请求 -> Node.js -> MySQL -> Node.js -> 响应

加入缓存后,常见路径变为:

请求 -> Node.js -> Redis 命中 -> 响应
                     |
                     +-- 未命中 -> MySQL -> 写入 Redis -> 响应

Redis 通常不是“替代 MySQL”,而是承担更适合内存系统的工作。

常见使用场景

1. 缓存

2. 会话与临时状态

这些数据天然需要过期时间。

3. 计数与限流

Redis 的 INCR 是原子命令,适合实现接口访问次数、阅读量和简单限流。

4. 排行榜

Sorted Set 能按照分数排序,适合积分榜、热度榜和延迟排名。

5. 去重和幂等

通过 SET key value NX EX seconds 可以实现“仅第一次成功”的短期标记,用于避免消息或请求被重复处理。

6. 消息通知

Pub/Sub 适合在线、瞬时通知。List、Stream 或专业消息队列更适合需要积压、确认和重试的任务。

什么时候不应该使用 Redis

初学者最重要的认识

Redis 中的数据可能消失

键可能因为 TTL 到期、内存淘汰、运维操作或故障而消失。缓存代码必须能够接受未命中,并从权威数据源重新构建。

Redis 很快,但网络并不免费

连续执行 1000 次 await client.get(...) 会产生大量往返。需要批量命令、Promise.all()、流水线或合适的数据结构。

原子命令比“先读后写”更可靠

错误示例:

const current = Number(await client.get("page:views") ?? "0");
await client.set("page:views", String(current + 1));

两个请求可能读到同一个旧值。应使用 Redis 原子命令:

await client.incr("page:views");

最佳实践

本章小结

Redis 的价值不是“比数据库高级”,而是在合适场景中用内存数据结构减少延迟、降低数据库压力,并提供原子操作。正确使用 Redis 的前提是先定义数据角色和失败时的行为。