3.3 Redis Cluster 集群
本节对应 PDF 第 3 章中 3.3 Redis Cluster 的内容,主要介绍:
- Redis Cluster 的背景与设计目标;
- 基于虚拟槽(hash slot)的数据分区机制;
- 集群内部的通信与路由;
- 集群的扩容与缩容;
- 部署与使用过程中的注意事项。
3.3.1 Redis Cluster 介绍
使用 Sentinel 可以解决主从架构中的高可用问题,实现 master 宕机时的自动故障转移,但仍有两个关键限制:
- 写入能力仍然受限于单个 master;
- 单机内存容量限制难以突破。
为了解决这些问题,社区较早期出现了两类分布式部署方案:
-
客户端分区:
- 客户端自行实现“按某种规则将键映射到不同 Redis 实例”的逻辑;
- 同时还要自己处理高可用与故障转移,客户端实现复杂且难以维护。
-
代理分区:
- 客户端只连一个代理(如 Twitter 的 Twemproxy、豌豆荚的 Codis);
- 代理负责将请求转发到不同的 Redis 实例;
- 代理本身容易成为性能瓶颈和新的单点。
自 Redis 3.0 起,官方推出了 无中心架构的 Redis Cluster:
- 支持多个 master 并行写入;
- 内置分片和故障转移机制;
- 不依赖额外的代理进程;
- 通过 gossip 协议在节点间传播拓扑信息。
3.3.2 Redis Cluster 架构
PDF 中给出的 Cluster 架构要点如下:
- 集群至少需要 3 个 master 节点 才能构成一个可用集群;
- 每个 master 可以挂载 0 个或多个 slave 节点,用于高可用;
- Master 节点必须有 超过半数 处于可用状态,集群才会对外提供服务;
- 为了便于投票与故障判定,master 的数量通常设置为奇数;
- 在生产环境中,常见的最小拓扑是 “3 master + 3 slave”。
示例:3 个 master 分担 16384 个槽位:
- 节点 M1:负责槽
0–5460; - 节点 M2:负责槽
5461–10922; - 节点 M3:负责槽
10923–16383。
一旦某个 master 未能通过多数节点的健康检查,其对应的某个 slave 会被提升为新的 master,继续负责原有槽位。
3.3.3 数据分区与虚拟槽
3.3.3.1 分区策略对比
在讨论 Redis Cluster 的实现前,先回顾数据分区的几种典型策略:
| 分区方式 | 特点 |
|---|---|
| 顺序分布 | 数据按范围划分,易于范围扫描,但热点易集中 |
| 简单哈希 | 使用键的哈希值取模,分布较均匀,但迁移成本较高 |
| 一致性哈希 | 对节点和数据都取哈希,扩缩容影响的键相对较少 |
顺序分布有利于按范围访问,但容易出现“热点分区”;简单哈希与一致性哈希则更强调负载均衡。
3.3.3.2 Redis Cluster 的虚拟槽分区
Redis Cluster 采用 虚拟槽(hash slot)分区:
- 集群一共定义了
0 ~ 16383共 16384 个槽位; - 每个槽位映射一部分键的集合;
- 每个 master 负责其中的一部分槽位;
- 通过迁移槽位即可在 master 之间平衡数据与负载。
键到槽的映射规则是:
- 对键名执行
CRC16(key)哈希函数,得到一个整数; - 对该整数取模
16384,得到最终槽位号; - 根据槽位号查出负责该槽的 master,将键存储到对应节点。
虽然 CRC16 本质上是错误检测码算法,但它具备“相同输入产生相同输出”的确定性特性,非常适合用作这种槽位映射的哈希函数。
通过虚拟槽层这一间接层,集群在扩容、缩容时只需迁移部分槽位及其对应的数据,而无需对所有键重新做一次全局哈希。
3.3.4 集群内部通信与路由
3.3.4.1 节点通信与拓扑感知
Cluster 节点之间通过 gossip 协议 交换信息,包括:
- 节点的存活状态;
- 槽位分配情况;
- 主从关系与故障标记等。
在节点加入集群时,典型过程是:
- 现有集群节点对新节点执行
CLUSTER MEET <ip> <port>握手; - 新节点收到握手后加入集群并开始与其他节点通讯;
- 随着 gossip 消息不断传播,所有节点都逐步感知到新成员及其负责的槽位。
3.3.4.2 客户端请求路由
Cluster 模式下,客户端通常具备以下能力:
- 根据键计算槽位号;
- 根据当前缓存的“槽 → 节点”映射表,将请求发送到相应节点;
- 当发送到的节点不是槽位所有者时:
- 节点返回
MOVED或ASK重定向响应; - 客户端更新本地映射表,并将请求重发到正确节点。
- 节点返回
得益于这种机制,即使槽位在节点间迁移,客户端在短暂重试之后也能重新命中目标节点。
3.3.5 集群扩容与缩容
Redis Cluster 的扩缩容本质上是“调整槽位与数据的归属”:
3.3.5.1 集群扩容
- 准备新节点:
- 安装并启动 Redis 实例(以 cluster 模式启动,监听新端口);
- 确保配置干净,无旧数据;
- 将新节点加入集群:
- 在现有节点上执行
CLUSTER MEET命令,或使用redis-cli --cluster add-node工具;
- 在现有节点上执行
- 重新分配槽位:
- 使用
redis-cli --cluster reshard或手动执行CLUSTER ADDSLOTS/CLUSTER DELSLOTS/CLUSTER SETSLOT等命令; - 将部分槽位从旧 master 迁移到新节点上;
- 使用
- 数据迁移:
- 在重新分配槽位的过程中,Redis 会自动把对应槽位的数据迁移到新节点。
3.3.5.2 集群缩容
缩容的流程与扩容相反:
- 将待下线节点负责的槽位迁移到其他 master;
- 确保该节点不再持有任何槽位;
- 执行
CLUSTER FORGET让集群忘记该节点; - 停止并下线该 Redis 实例。
在整个过程中,gossip 协议会同步槽位迁移与节点变更,让集群始终保持一致视图。
3.3.6 部署与使用建议
结合 PDF 描述与实践经验,部署 Redis Cluster 时可以参考以下建议:
-
节点必须是“干净”的:
- 建集群前清空所有参与节点的数据;
- 不要在已有持久化数据的实例上直接启用 cluster 模式,以免产生混乱。
-
Master 数量设置为奇数:
- 有利于多数派投票与故障判定;
- 常见的规模有 3、5、7 个 master。
-
每个 master 至少配置一个 slave:
- master 故障时可以自动由 slave 接管;
- 读压力较大时还可将部分读请求发送到 slave。
-
不要在 Cluster 节点上再叠加 Sentinel 主从架构:
- Cluster 自身已经提供高可用能力,无需再使用 Sentinel;
- Sentinel 与 Cluster 同时作用在同一组节点上可能造成冲突与不可预期行为。
-
使用支持 Cluster 的客户端:
- 客户端需要理解 MOVED/ASK 重定向才能正确工作;
- 或者使用支持 Cluster 的中间件或代理。
-
充分演练故障与扩缩容场景:
- 在准生产或测试环境中模拟 master 宕机、网络分区、节点加入与移除;
- 观察客户端行为、数据一致性与性能表现,完善监控与报警。
通过 Redis Cluster,可以在保持高可用的基础上实现水平扩展,大幅提升 Redis 服务的容量与吞吐能力,是中大型系统中常见的分布式缓存/存储方案。