第 29 章 / 共 57 章 · 约 4 分钟
关键技术:Redis
本页目录
- 1. 核心模型
- 2. 什么时候用
- 3. 什么时候不适合
- 4. 关键限制和数量级
- 5. 面试中常被追问的点
- 6. 和其他技术的取舍
- 7. 本站案例
- 章节测验
Redis 是一个把数据全部放在内存里的数据结构服务器。它不只是键值缓存:字符串、哈希、列表、集合、有序集合、流、地理位置都是内置类型,每条命令在服务端原子执行。系统设计里,缓存、计数器、排行榜、限流、分布式锁、在线状态,几乎都会想到它。
1. 核心模型
- 单线程执行命令:命令在一个主线程里依次执行(网络 I/O 可以多线程),所以单条命令天然原子,
INCR 不会有并发丢更新。需要“读-判断-写”这种多步原子操作时,用 Lua 脚本或 MULTI/EXEC。
- 数据结构即接口:选对结构,很多功能一条命令就能完成。
| 结构 | 典型用途 | 代表命令 |
|---|
| String | 缓存值、计数器、锁 | GET/SET、INCR、SET key val NX PX 30000 |
| Hash | 对象的多个字段 | HSET、HINCRBY |
| Sorted Set | 排行榜、按时间排序的时间线、延迟队列 | ZADD、ZREVRANGE、ZRANGEBYSCORE |
| Set / HyperLogLog | 去重、基数估算 | SADD、PFADD(约 12 KB,标准误差约 0.81%) |
| Stream | 带消费者组的持久消息流 | XADD、XREADGROUP |
| Geo | 附近的人 / 车 | GEOADD、GEOSEARCH |
- 持久化:RDB 定期快照,AOF 追加写命令日志(常用每秒 fsync,最多丢约 1 秒数据)。即便开了持久化,Redis 一般也不当作唯一的数据来源。
2. 什么时候用
- 缓存:cache-aside 读路径,详见基本构件里的缓存一节。
- 计数和限流:
INCR 加过期时间做固定窗口;滑动窗口、令牌桶用 Lua 脚本保证原子性,见限流器案例。
- 排行榜和 Top-K:有序集合按分数排序,取前 100 名是 O(log N + 100)。
- 短期状态:会话、在线状态、验证码、幂等键,靠 TTL 自动过期。
- 轻量锁和预占:抢座的临时预占、司机派单时的锁,带 TTL 防止持有者崩溃后锁永不释放。
3. 什么时候不适合
- 数据量远大于内存预算:所有数据都要放内存,几 TB 的冷数据放 Redis 成本很高。
- 需要复杂查询或事务:没有 JOIN、没有二级索引(要自己维护),也没有跨 key 的回滚事务。
- 不能丢的业务数据:主从复制是异步的,主节点在复制前宕机,已经确认的写入可能丢失。
- 严格正确的分布式锁:单节点锁在故障切换时可能被两个客户端同时持有。正确性要求高时,要么配合 fencing token,要么用 ZooKeeper / etcd。
4. 关键限制和数量级
| 维度 | 要记住的事 |
|---|
| 吞吐 | 单实例每秒约 10 万量级的简单操作,延迟在亚毫秒级 |
| 容量 | 单节点通常几 GB 到几十 GB;超过就要分片 |
| 集群 | Redis Cluster 有 16384 个哈希槽,按 key 分到各个分片;多 key 命令要求这些 key 在同一个槽,可以用哈希标签 {user42} 强制放在一起 |
| 复制 | 异步复制;Sentinel 或 Cluster 负责故障切换,切换时可能丢掉最近的写入 |
| 淘汰 | 内存满了按策略淘汰:allkeys-lru、allkeys-lfu、volatile-ttl 等;默认 noeviction 直接拒绝写入 |
| 慢命令 | KEYS *、对超大集合的 SMEMBERS、删除超大 key 都会阻塞主线程;用 SCAN、UNLINK 代替 |
5. 面试中常被追问的点
- 缓存和数据库怎么保持一致? 先写库再删缓存,配合 TTL 兜底;残留的竞态窗口可以用延迟双删或者 CDC 驱动的失效来收窄。
- 热点 key 怎么办? 单个 key 的流量超过一个分片的能力时,在应用进程里加一层本地缓存,或者把 key 复制成
key#1..key#N 分散到多个分片上读。
- 缓存击穿和雪崩? 热 key 过期时用互斥锁或 single-flight 合并回源请求;TTL 加随机抖动,避免大量 key 同时过期。
- 分布式锁靠谱吗?
SET key token NX PX ttl 加锁,释放时用 Lua 先比较 token 再删除,防止误删别人的锁。持锁进程可能因为 GC 暂停超过 TTL,所以写共享资源时要带单调递增的 fencing token,由存储端拒绝旧 token。
- Pub/Sub 能当消息队列吗? 不能可靠地用:订阅者不在线时消息直接丢。需要持久化和消费确认就用 Stream 或 Kafka。
6. 和其他技术的取舍
| 选项 | 适合 | 代价 |
|---|
| Redis | 丰富数据结构、原子操作、低延迟 | 内存成本;异步复制可能丢写 |
| Memcached | 纯缓存,多线程,简单 | 只有字符串,没有持久化和复制 |
| 进程内缓存 | 最热的数据,零网络开销 | 各实例之间不一致,容量小 |
| DynamoDB / Cassandra | 大数据量、要持久的键值访问 | 延迟是毫秒级,不是亚毫秒 |
7. 本站案例