Redis 集群是 Redis 提供的分布式解决方案,旨在解决单机 Redis 在数据量、吞吐量和高可用性方面的瓶颈。其核心原理围绕着数据分片(Sharding) 、节点间通信(Gossip 协议) 、故障检测与转移(Failover) 以及客户端路由来实现。以下是详细解析:
✨ 1. 数据分片(Sharding)
- 槽位(Slot): Redis 集群将整个数据集划分为 16384 个哈希槽。这是集群的核心抽象。
- 键到槽的映射:
- 客户端通过公式
HASH_SLOT = CRC16(key) % 16384
计算键key
所属的槽位。 CRC16(key)
计算 key 的 CRC16 校验码。- 结果对 16384 取模,确定槽位号(0-16383)。
- 客户端通过公式
- 槽位分配:
- 集群启动或变更(如增删节点)时,管理员或工具(如
redis-cli --cluster
)负责将 16384 个槽位分配给集群中的主节点。 - 分配策略应尽可能均匀(例如,3主节点各分约5461个槽)。
- 每个主节点负责处理映射到其槽位上的所有键的读写操作。
- 关键点: 集群的状态包含一个
槽位映射表
,记录每个槽位当前由哪个主节点负责。
- 集群启动或变更(如增删节点)时,管理员或工具(如
🔄 2. 节点角色与架构
- 主节点(Master): 负责存储数据(处理分配给它的槽位的数据),处理读写请求,参与故障转移投票。
- 从节点(Replica / Slave): 复制一个主节点的数据(异步复制)。主要作用是故障转移(Failover):当它复制的主节点宕机时,符合条件的从节点可以晋升为新的主节点,接管原主节点的槽位,保证服务可用性。它也处理读请求(分担负载)。
- 集群模式: 最小规模通常为 3 个主节点 + 3 个从节点(每个主节点有一个从节点),提供基本的分片和容错能力。
- 去中心化: Redis 集群是无中心节点设计的。每个节点都存储完整的集群状态信息(槽位映射、节点信息等),并通过 Gossip 协议与其他节点通信。
📡 3. 节点间通信 - Gossip 协议
- Cluster Bus: 每个节点都有一个额外的 TCP 端口(默认端口号 + 10000,如 16379),专门用于节点间通信。这条通道称为集群总线(Cluster Bus)。
- Gossip 协议工作方式:
- 节点间定期(每秒多次)随机选择少量(通常为
cluster-node-timeout / 2
时间内未通信过的节点)其他节点进行通信。 - 通信内容(PING/PONG/PFAIL/FAIL 消息)包含:
- 自身的信息(ID、角色、负责的槽位、纪元等)。
- 它所知道的其他节点的信息(包括状态:在线、疑似下线、已下线)。
- 集群当前的配置纪元(Configuration Epoch)。
- 信息通过这种"闲聊"的方式在整个集群中指数级快速传播。
- 节点间定期(每秒多次)随机选择少量(通常为
- 作用:
- 发现节点: 新节点加入时,通过已知节点快速发现集群中所有其他节点。
- 传播状态: 广播节点上线、下线、槽位分配变更等状态信息。
- 维护集群视图一致性: 最终使所有节点对集群拓扑和状态达成一致(最终一致性)。
- 故障检测基础: 为故障检测提供心跳机制。
4. 故障检测与转移(Failover)
- 主观下线(PFAIL - Possibly Fail):
- 节点 A 通过 Cluster Bus 在
cluster-node-timeout
(可配置,默认15秒) 时间内未能收到节点 B 的 PING 或 PONG 响应。 - 节点 A 在自己的集群状态中将节点 B 标记为
PFAIL
,并通过 Gossip 消息告诉其他节点"我认为B可能挂了"。
- 节点 A 通过 Cluster Bus 在
- 客观下线(FAIL):
- 集群中大多数主节点 (超过 N/2 + 1,N为当前存活主节点数)都在自己的状态中将节点 B 标记为
PFAIL
。 - 一旦达成共识,首个检测到这一多数派达成情况的主节点 会立即将节点 B 的状态升级为
FAIL
。 - 该主节点向整个集群广播一条
FAIL
消息,强制所有收到消息的节点都将节点 B 标记为FAIL
(客观下线)。
- 集群中大多数主节点 (超过 N/2 + 1,N为当前存活主节点数)都在自己的状态中将节点 B 标记为
- 从节点晋升(故障转移):
- 当一个主节点被标记为
FAIL
(客观下线),其下属的一个从节点会发起故障转移流程成为新的主节点。 - 触发条件: 从节点发现自己复制的主节点进入
FAIL
状态。 - 竞选资格: 从节点会等待一小段延迟时间(通常为主节点宕机时间 + 固定偏移量,目的是让数据更完整的从节点有机会赢得选举),然后尝试发起选举。
- 投票过程(Raft-like):
- 从节点向集群中所有主节点 广播
CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST
请求投票。 - 主节点收到请求后,在一个配置纪元(epoch)内只能投一次票。
- 主节点检查请求是否符合条件(主节点确实FAIL了;请求者的主节点是FAIL节点;该主节点本轮epoch尚未投票)。
- 如果符合条件,主节点回复
CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
表示同意。
- 从节点向集群中所有主节点 广播
- 赢得选举: 从节点收集到大多数(超过 N/2 + 1)主节点的投票。
- 成为新主节点:
- 赢得投票的从节点晋升为新主节点。
- 它接管旧主节点负责的所有槽位。
- 广播
PONG
消息通知集群所有节点它的新角色和负责的槽位,更新集群视图。 - 其他节点更新槽位映射表,指向新主节点。
- 新主节点开始处理读写请求。
- 如果旧主节点恢复,它会发现自己的槽位已被接管,并自动变为新主节点的从节点(需配置
cluster-slave-no-failover
为默认值 no)。
- 当一个主节点被标记为
5. 客户端请求处理(重定向)
Redis 集群客户端需要了解集群拓扑(槽位分布)。工作流程:
- 客户端连接任意节点。
- 客户端发送请求: 如
GET mykey
。 - 节点计算槽位: 节点计算
mykey
的槽位H
。 - 判断槽位归属:
- Case 1: 槽位
H
由本节点负责: 节点直接执行命令并返回结果给客户端。 - Case 2: 槽位
H
不由本节点负责:- MOVED 重定向: 如果槽位迁移未在进行中,节点返回
-MOVED <slot> <ip>:<port>
错误给客户端。错误信息包含正确节点的地址(IP:PORT)。客户端必须更新本地槽位缓存,并向正确节点重新发送请求。 - ASK 重定向: 如果该槽位正处于从一个节点(源节点)迁移到另一个节点(目标节点)的过程中:
- 源节点收到请求时,如果
mykey
还在自己这里,则处理。 - 如果
mykey
已被迁移到了目标节点,源节点返回-ASK <slot> <target-ip>:<target-port>
错误。 - 客户端收到
ASK
错误后:- 先向目标节点发送一个
ASKING
命令(临时标志)。 - 然后立即重发
GET mykey
命令到目标节点。 - 目标节点看到
ASKING
标志后,即使槽位尚未完全迁移过来,也会处理这个针对该槽位的命令。
- 先向目标节点发送一个
ASK
重定向仅影响本次命令 ,客户端不需要更新本地槽位缓存。迁移完成后,后续请求会收到MOVED
重定向或直接由新节点处理。
- 源节点收到请求时,如果
- MOVED 重定向: 如果槽位迁移未在进行中,节点返回
- Case 1: 槽位
🔧 6. 集群管理与运维
- 节点操作:
- 添加节点: 新节点作为空节点加入集群。然后使用
CLUSTER MEET
命令或工具将其加入集群视图。 - 删除节点: 若节点是主节点且持有槽位,需先将其槽位迁移给其他节点。若是从节点或无槽主节点,可直接删除。
- 主节点下线: 需手动触发故障转移(
CLUSTER FAILOVER
)让其从节点接替,然后安全下线。
- 添加节点: 新节点作为空节点加入集群。然后使用
- 槽位迁移:
- 扩容/缩容/负载均衡时,需要将槽位从一个主节点迁移到另一个主节点。
- 使用
CLUSTER SETSLOT <slot> IMPORTING <source-node-id>
(目标节点)和CLUSTER SETSLOT <slot> MIGRATING <target-node-id>
(源节点)命令设置迁移状态。 - 使用
MIGRATE
命令或工具(redis-cli --cluster reshard
)逐个键地将数据从源节点迁移到目标节点(原子操作)。 - 迁移过程中,客户端可能遇到
ASK
重定向。 - 迁移完成所有键后,广播槽位映射更新(
CLUSTER SETSLOT <slot> NODE <target-node-id>
)。
- 从节点重新配置: 更改从节点的主节点。
- 配置纪元(Configuration Epoch):
- 一个单调递增的计数器,充当集群操作的逻辑时钟。
- 用于保证故障转移和槽位迁移操作的顺序性和唯一性。例如,拥有更高配置纪元的新主节点在冲突时获胜。
📌 总结 Redis 集群的关键原理
模块 | 核心机制 | 关键特点 |
---|---|---|
数据分布 | 16384槽位 + CRC16分片 | 水平扩展的理论基础 |
节点架构 | 主从复制 + 自动故障转移 | 兼顾性能与高可用 |
通信协议 | Gossip广播 + 集群总线 | 去中心化拓扑维护 |
故障恢复 | 主观下线→客观下线→投票选举 | 基于Raft的共识机制 |
客户端交互 | MOVED永久重定向/ASK临时重定向 | 智能路由的保障机制 |
⚠ 重要限制
- 集群模式下不支持多数据库 (只能使用
DB 0
)。 - 涉及多个键的操作(如
MGET
,MSET
,SUNION
, 事务MULTI/EXEC
, Lua脚本)要求所有键必须在同一个节点上 (即同一个哈希槽,除非使用哈希标签{}
)。 - 客户端库需要支持集群协议(重定向处理、槽位缓存、智能路由)。
💡 运维视角
- 16384槽位:这个精心选择的数字平衡了网络开销与数据分布粒度
- cluster-node-timeout:核心参数,影响故障检测速度和网络分区容忍度
- 主节点数量:决定了故障转移的投票门槛(N/2+1)
- 从节点作用:既是数据冗余,也是故障转移的"备胎"
Redis 集群通过巧妙的分片设计、高效的 Gossip 通信、可靠的故障检测与基于多数派投票的故障转移机制,实现了高性能、高可用和可扩展的分布式数据存储解决方案。理解其槽位分配、Gossip协议、重定向机制以及故障转移流程是掌握 Redis 集群原理的关键。