跳到主要内容

3.3 Redis Cluster 集群

本节对应 PDF 第 3 章中 3.3 Redis Cluster 的内容,主要介绍:

  • Redis Cluster 的背景与设计目标;
  • 基于虚拟槽(hash slot)的数据分区机制;
  • 集群内部的通信与路由;
  • 集群的扩容与缩容;
  • 部署与使用过程中的注意事项。

3.3.1 Redis Cluster 介绍

使用 Sentinel 可以解决主从架构中的高可用问题,实现 master 宕机时的自动故障转移,但仍有两个关键限制:

  1. 写入能力仍然受限于单个 master
  2. 单机内存容量限制难以突破

为了解决这些问题,社区较早期出现了两类分布式部署方案:

  • 客户端分区

    • 客户端自行实现“按某种规则将键映射到不同 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 之间平衡数据与负载。

键到槽的映射规则是:

  1. 对键名执行 CRC16(key) 哈希函数,得到一个整数;
  2. 对该整数取模 16384,得到最终槽位号;
  3. 根据槽位号查出负责该槽的 master,将键存储到对应节点。

虽然 CRC16 本质上是错误检测码算法,但它具备“相同输入产生相同输出”的确定性特性,非常适合用作这种槽位映射的哈希函数。

通过虚拟槽层这一间接层,集群在扩容、缩容时只需迁移部分槽位及其对应的数据,而无需对所有键重新做一次全局哈希。


3.3.4 集群内部通信与路由

3.3.4.1 节点通信与拓扑感知

Cluster 节点之间通过 gossip 协议 交换信息,包括:

  • 节点的存活状态;
  • 槽位分配情况;
  • 主从关系与故障标记等。

在节点加入集群时,典型过程是:

  1. 现有集群节点对新节点执行 CLUSTER MEET <ip> <port> 握手;
  2. 新节点收到握手后加入集群并开始与其他节点通讯;
  3. 随着 gossip 消息不断传播,所有节点都逐步感知到新成员及其负责的槽位。

3.3.4.2 客户端请求路由

Cluster 模式下,客户端通常具备以下能力:

  1. 根据键计算槽位号;
  2. 根据当前缓存的“槽 → 节点”映射表,将请求发送到相应节点;
  3. 当发送到的节点不是槽位所有者时:
    • 节点返回 MOVEDASK 重定向响应;
    • 客户端更新本地映射表,并将请求重发到正确节点。

得益于这种机制,即使槽位在节点间迁移,客户端在短暂重试之后也能重新命中目标节点。


3.3.5 集群扩容与缩容

Redis Cluster 的扩缩容本质上是“调整槽位与数据的归属”:

3.3.5.1 集群扩容

  1. 准备新节点
    • 安装并启动 Redis 实例(以 cluster 模式启动,监听新端口);
    • 确保配置干净,无旧数据;
  2. 将新节点加入集群
    • 在现有节点上执行 CLUSTER MEET 命令,或使用 redis-cli --cluster add-node 工具;
  3. 重新分配槽位
    • 使用 redis-cli --cluster reshard 或手动执行 CLUSTER ADDSLOTS / CLUSTER DELSLOTS / CLUSTER SETSLOT 等命令;
    • 将部分槽位从旧 master 迁移到新节点上;
  4. 数据迁移
    • 在重新分配槽位的过程中,Redis 会自动把对应槽位的数据迁移到新节点。

3.3.5.2 集群缩容

缩容的流程与扩容相反:

  1. 将待下线节点负责的槽位迁移到其他 master;
  2. 确保该节点不再持有任何槽位;
  3. 执行 CLUSTER FORGET 让集群忘记该节点;
  4. 停止并下线该 Redis 实例。

在整个过程中,gossip 协议会同步槽位迁移与节点变更,让集群始终保持一致视图。


3.3.6 部署与使用建议

结合 PDF 描述与实践经验,部署 Redis Cluster 时可以参考以下建议:

  1. 节点必须是“干净”的

    • 建集群前清空所有参与节点的数据;
    • 不要在已有持久化数据的实例上直接启用 cluster 模式,以免产生混乱。
  2. Master 数量设置为奇数

    • 有利于多数派投票与故障判定;
    • 常见的规模有 3、5、7 个 master。
  3. 每个 master 至少配置一个 slave

    • master 故障时可以自动由 slave 接管;
    • 读压力较大时还可将部分读请求发送到 slave。
  4. 不要在 Cluster 节点上再叠加 Sentinel 主从架构

    • Cluster 自身已经提供高可用能力,无需再使用 Sentinel;
    • Sentinel 与 Cluster 同时作用在同一组节点上可能造成冲突与不可预期行为。
  5. 使用支持 Cluster 的客户端

    • 客户端需要理解 MOVED/ASK 重定向才能正确工作;
    • 或者使用支持 Cluster 的中间件或代理。
  6. 充分演练故障与扩缩容场景

    • 在准生产或测试环境中模拟 master 宕机、网络分区、节点加入与移除;
    • 观察客户端行为、数据一致性与性能表现,完善监控与报警。

通过 Redis Cluster,可以在保持高可用的基础上实现水平扩展,大幅提升 Redis 服务的容量与吞吐能力,是中大型系统中常见的分布式缓存/存储方案。