Redis 核心原理:缓存、持久化与集群
系统梳理 Redis 数据结构、缓存三大问题、热 Key/大 Key、持久化、分布式锁和 Cluster。
一、Redis 为什么快
Redis 的高性能来自多项设计共同作用:
- 数据主要在内存中访问;
- 命令执行路径短,大多数操作复杂度低;
- 核心命令串行执行,避免大量锁竞争和上下文切换;
- I/O 多路复用可管理大量连接;
- SDS、dict、listpack、quicklist、skiplist 等结构针对使用场景优化;
- RESP 协议简单。
“Redis 是单线程”不够准确。核心命令执行长期以单线程模型为主,但持久化、异步释放、复制等工作会使用后台线程或进程;Redis 6 以后还可使用 I/O 线程处理部分网络读写。
二、常用数据类型与选型
String
用于普通缓存、计数器、Token、开关和分布式锁。底层字符串使用 SDS,支持二进制安全和高效长度获取。
Hash
适合按字段更新对象,但字段过多会形成大 Key。对象很小、整体读取频繁时,序列化后的 String 也可能更简单。
List
适合双端队列和有限长度列表。简单队列可以使用 List,但需要消费组、ACK、回溯时更适合 Stream 或专业 MQ。
Set
适合去重、集合关系、标签和成员判断。
ZSet
元素唯一并携带 score,适合排行榜、TopN 和按时间排序。小数据量可使用紧凑结构,规模扩大后通常由跳表和字典共同支持排序与定位。
Stream
支持消息 ID、消费组、ACK 和待处理消息列表,可做轻量消息流。但跨系统可靠消息、长期堆积和复杂治理通常仍应选择 Kafka、RocketMQ 等专业 MQ。
三、穿透、击穿与雪崩
| 问题 | 核心特征 | 主要方案 |
|---|---|---|
| 缓存穿透 | 缓存和数据库都不存在 | 参数校验、布隆过滤器、空值缓存、限流风控 |
| 缓存击穿 | 单个热点 Key 失效 | 逻辑过期、互斥重建、提前刷新、降级 |
| 缓存雪崩 | 大量 Key 集中过期或 Redis 整体不可用 | TTL 随机化、多级缓存、预热、高可用、限流熔断 |
缓存穿透
布隆过滤器判断“不存在”时可以直接拦截;判断“可能存在”仍需查询。它存在误判,不存在漏判。缓存空值要设置较短 TTL,既避免重复回源,也防止随机无效 Key 长期占用内存。
互斥锁只适合大量请求集中在同一个不存在 Key 的情况;面对大量随机 Key,参数校验、布隆过滤器和风控更有效。
缓存击穿
高并发热点数据可使用逻辑过期:Key 不做物理删除,Value 中保存逻辑过期时间;过期后先返回旧值,再由一个线程异步重建。
如果不能接受旧值,可以用本地锁或分布式锁限制同一 Key 的回源并发。拿到锁后必须二次检查缓存,避免等待期间其他线程已经完成回填。
缓存雪崩
雪崩不只有“大量 Key 同时过期”,还包括 Redis 集群整体不可用。治理需要覆盖两个层面:
- 缓存层:TTL 随机化、异步刷新、逻辑过期、缓存预热、多级缓存;
- 系统层:限流、熔断、降级、数据库保护和 Redis 高可用。
四、缓存与数据库一致性
Cache Aside 的常用更新策略是:
更新数据库
↓
删除缓存
通常删除缓存而不是直接更新,是因为缓存可能是聚合结果,并发更新容易互相覆盖,且部分缓存可能根本不会再次被读取。
删除失败可通过:
- 有限重试;
- 投递补偿消息;
- 监听 binlog/CDC 删除缓存;
- TTL 最终过期兜底。
它通常提供最终一致性,而不是强一致性。资金余额等强一致数据不能把缓存当权威数据源,核心约束仍应落在数据库事务、唯一键、状态机或条件更新上。
五、热 Key 如何识别与治理
热 Key 是访问流量集中,而不是 Value 很大。判断依据包括:
- 云监控或代理层的单 Key QPS TopN;
- 客户端采样统计;
- Cluster 节点 CPU、带宽和延迟明显倾斜;
- LFU 条件下使用
redis-cli --hotkeys辅助发现; - 结合赛事、秒杀、首页配置等业务提前识别。
MONITOR 开销大,不应在线上长期启用。
读热点
优先考虑本地缓存和多级缓存:
Caffeine → Redis → 数据库
也可以创建多个内容相同的副本 Key,并让它们在 Cluster 中落到不同节点。这个方案适合读多写少、允许短暂最终一致的数据;单机 Redis 中只拆 Key 并不能分散节点 CPU 和带宽。
写热点
不能简单复制后随机写。需要根据业务采用分片计数、本地聚合、消息队列削峰、串行化更新或重新设计数据模型,并明确最终汇总和一致性规则。
六、大 Key 如何识别与治理
大 Key 指 Value 占用内存过大,或集合元素过多。可通过以下方式识别:
redis-cli --bigkeys
redis-cli --memkeys
MEMORY USAGE key
HLEN / LLEN / SCARD / ZCARD / XLEN
慢日志与监控平台
治理原则:
- 按时间、用户、租户或哈希分片;
- 限制集合最大长度,历史数据归档;
- 避免
HGETALL、SMEMBERS、LRANGE 0 -1; - 使用
HSCAN、SSCAN、ZSCAN渐进遍历,List 分段读取; - 整 Key 删除优先使用
UNLINK,并在低峰期分批限速。
UNLINK 会先从 Keyspace 摘除 Key,再由后台线程释放 Value 内存。因此 Key 立即不可见,但 used_memory 可能不会立刻下降。它减少主线程阻塞,不代表删除没有成本。
七、持久化
RDB
RDB 是某个时间点的快照。文件紧凑、恢复快,适合备份;缺点是可能丢失两次快照之间的数据,fork 和写时复制也可能造成内存和延迟压力。
AOF
AOF 记录写命令。常见刷盘策略:
always:每次写都刷盘,可靠性高、性能成本大;everysec:通常最多承受约一秒窗口的数据丢失,常用折中;no:交给操作系统决定刷盘。
AOF rewrite 会把历史操作压缩为能恢复当前状态的更短命令序列。
混合持久化
重写后的 AOF 前部使用 RDB 格式保存全量快照,后部追加增量命令,兼顾恢复速度与数据安全。
八、主从、哨兵与 Cluster
更准确的定位是:
- 主从复制:数据冗余和读扩展,不能提升单主写吞吐;
- Sentinel:在主从架构上增加监控、选主和自动故障转移;
- Cluster:通过多个 Master 分片实现容量和写吞吐扩展,同时通过副本提供故障转移。
Cluster 分片
Redis Cluster 把 Keyspace 划分为 16384 个槽:
slot = CRC16(key) mod 16384
客户端维护 slot → node 路由。MOVED 表示槽归属已经改变,应更新路由;ASK 表示槽正在迁移,这次临时访问目标节点。
Hash Tag 可让多个 Key 落到同一槽:
user:{1001}:name
user:{1001}:age
它有利于多 Key 操作,但过度使用会造成槽位热点。
九、Redis 分布式锁
基础加锁:
SET lock:key unique-token NX PX 30000
释放锁必须用 Lua 原子地判断 Token 并删除,避免业务执行超时后误删其他线程新获得的锁。
Redisson Watchdog 可以在未指定固定租约时自动续期,但仍应考虑:
- JVM 长暂停或网络分区;
- 主从异步复制与故障切换窗口;
- 锁过期后旧线程继续执行;
- 业务操作是否具有幂等和 fencing token 保护。
Redis 锁不是资金一致性的唯一防线。数据库唯一约束、条件更新、幂等号和状态机仍然必要。
十、速记结论
穿透是查不存在的数据,击穿是一个热点 Key 失效,雪崩是大量 Key 失效或 Redis 整体不可用;热 Key 治理访问集中,大 Key 治理数据体积;主从做读扩展和副本,哨兵做自动故障转移,Cluster 做分片扩展和高可用。
评论与讨论
回复 :
留下你的想法