中间件

Redis 核心原理:缓存、持久化与集群

系统梳理 Redis 数据结构、缓存三大问题、热 Key/大 Key、持久化、分布式锁和 Cluster。

难度:深入更新:2026-07-16

一、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
慢日志与监控平台

治理原则:

  1. 按时间、用户、租户或哈希分片;
  2. 限制集合最大长度,历史数据归档;
  3. 避免 HGETALLSMEMBERSLRANGE 0 -1
  4. 使用 HSCANSSCANZSCAN 渐进遍历,List 分段读取;
  5. 整 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 做分片扩展和高可用。

参考资料

DISCUSSION

评论与讨论

留下你的想法