6.9 KiB
tags, create time
| tags | create time | |||||
|---|---|---|---|---|---|---|
|
2026-05-27 10:00 |
Raft 共识算法
概述
Raft 是一种分布式共识算法,用于让多个节点就某个值达成一致。它的设计目标是比 Paxos 更容易理解和实现。Redis Cluster 的故障转移(Failover)借鉴了 Raft 的领导者选举机制来选出新的 Master。
[!NOTE] "Raft 式投票"不等于完整 Raft Redis Cluster 只使用了 Raft 的选举(Leader Election) 部分,并没有使用完整的 Raft 日志复制机制。Redis 的数据复制走的是自身的异步复制协议。所以文档中说的是"Raft 式投票"。
一、为什么需要共识?
在分布式系统中,当一个节点宕机时,其他节点需要达成一致:"这个节点确实挂了",并且"由谁来接替它"。如果各节点自行判断、自行行动,就会出现混乱——比如两个节点同时认为自己应该接管服务。
[!QUESTION] 直觉思考 一个 3 人小组的组长突然失踪了。剩下的两个人可以投票选出新组长。但如果 6 人小组刚好分成两个 3 人小组,两组各自选出了一个新组长——怎么办?这就是共识算法要解决的核心问题:在有故障和网络分区的情况下,如何让大多数节点达成一致决定?
二、Raft 选举核心机制
Raft 将节点分为三种角色:
- Leader(领导者):负责处理所有写请求,向 Follower 复制日志
- Follower(追随者):被动接收 Leader 的心跳和日志
- Candidate(候选人):Follower 超时未收到心跳后,临时切换的角色,负责发起选举请求。如果赢得多数票则晋升为 Leader,失败则退回 Follower
[!NOTE] 角色转换 所有节点初始都是 Follower。正常运行时只有 Follower → Candidate → Leader 这条路径。集群稳定后 Leader 持续发送心跳,Follower 保持被动,不会发起选举。
当 Follower 在一段时间内没有收到 Leader 的心跳,就会发起选举:
sequenceDiagram
participant F1 as Follower 1
participant F2 as Follower 2
participant F3 as Follower 3
Note over F1,F3: Leader 宕机,Follower 超时
F1->>F1: 超时,升级为 Candidate
F1->>F2: RequestVote (我的 term=2, 日志最新)
F1->>F3: RequestVote (我的 term=2, 日志最新)
F2->>F1: VoteGranted (同意)
F3->>F1: VoteGranted (同意)
Note over F1: 获得多数票<br/>成为新 Leader
F1->>F2: AppendEntries (心跳, 我是 Leader)
F1->>F3: AppendEntries (心跳, 我是 Leader)
关键概念
[!QUESTION] 如果两个 Follower 同时超时会怎样? 它们会同时变成 Candidate 并各自向对方拉票——谁都拿不到多数票,选举失败。Raft 的解决办法是随机化选举超时:每个节点的超时时间在
[T, 2T]区间内随机选取(T 通常为 150~300ms)。这样第一个超时的节点大概率会率先完成选举,避免了"选票被瓜分"的僵局。
| 概念 | 含义 | Redis Cluster 中的对应 |
|---|---|---|
| Term(任期) | 逻辑时钟,每次选举递增 | Epoch(集群纪元) |
| Election Timeout(选举超时) | Follower 等待心跳的超时时间,超时则发起选举 | cluster-node-timeout(默认 15s) |
| Heartbeat(心跳) | Leader 定期向 Follower 发送 AppendEntries RPC 维持地位 | Master → Replica 的 PING/PONG |
| RequestVote | Candidate 请求投票的 RPC | Replica 向 Master 发起投票请求 |
| 多数派(Quorum) | 超过半数节点同意 | 多数 Master 节点投票 |
| 先到先得 | 每个节点在一个 term 内只能投一票 | 同一个 epoch 内,每个 Master 只投一票 |
| 随机化超时 | 每个节点的选举超时设置为一个随机值,避免同时发起选举 | Replica 选举延迟与复制偏移量挂钩 |
[!TIP] 为什么需要"多数派"? 在 N 个节点中,任何两个"多数派"一定有交集。这意味着不可能同时存在两个都获得了多数票的 Candidate——保证了最多只有一个 Leader。这是 Raft 正确性的基石。
数学上:如果 N=3,多数派是 2;如果 N=5,多数派是 3。这也解释了为什么 Redis Cluster 建议使用奇数个 Master——4 个 Master 的多数派是 3,只能容忍 1 个故障(和 3 个 Master 的容错能力完全相同),但多了一个节点的开销;而 5 个 Master 能容忍 2 个故障,容错能力真正提升了。
三、Redis Cluster 的 Failover 选举
Redis Cluster 将 Raft 选举简化应用到故障转移场景:
flowchart TD
A["Master 宕机"] --> B["Replica 超时未收到心跳"]
B --> C["Replica 将 epoch + 1"]
C --> D["向所有 Master 请求投票"]
D --> E{"获得多数票?"}
E -->|是| F["提升为新 Master<br/>接管 slot 并广播"]
E -->|否| G["等待随机超时后重试"]
style A fill:#fce4ec,stroke:#e91e63
style F fill:#e8f5e9,stroke:#4caf50
style G fill:#fff3e0,stroke:#ff9800
详细步骤:
- 超时检测:Replica 发现 Master 超过
cluster-node-timeout未响应 PONG - 纪元递增:Replica 将当前 epoch 加 1(类似 Raft 的 term 递增)
- 发起投票:向集群中所有 Master 节点发送投票请求
- 投票规则:每个 Master 在同一个 epoch 内只投一票,先到先得
- 赢得选举:获得多数 Master 投票的 Replica 升级为 Master
- 广播新配置:新 Master 接管旧 Master 的所有 slot,通知集群所有节点
[!WARNING] 选举失败怎么办? 如果多个 Replica 同时发起选举且票数被瓜分,谁都拿不到多数票——选举失败。Redis Cluster 的解决方案是随机退避:每个 Replica 等待一个随机时间后再发起下一轮选举,减少再次冲突的概率。
Redis 的巧妙设计:这个随机延迟并非完全随机,而是与 Replica 的复制偏移量(replication offset) 挂钩——数据越新的 Replica,等待时间越短(
DELAY_FACTOR * 固定延迟 + 随机抖动)。这保证了数据最新的 Replica 大概率优先发起选举,从而最大程度减少数据丢失。
四、与 Paxos 的简要对比
| 特性 | Raft | Paxos |
|---|---|---|
| 可理解性 | 相对简单,角色清晰 | 极其晦涩,号称"只有一个人理解 Paxos" |
| 工程实现 | etcd、Consul、Redis(部分) | Google Chubby、Spanner |
| 适用场景 | Leader 选举 + 日志复制 | 任意值的共识 |
| 活跃节点要求 | 需要多数派存活 | 需要多数派存活 |
关联笔记
- hhs/Redis/07-集群方案 — Redis Cluster 集群方案
- hhs/Redis/07-集群方案/脑裂问题 — 当出现两个 Leader 时怎么办
- hhs/Redis/07-集群方案/Gossip协议 — 节点间如何互相发现和交换状态
- hhs/Redis/07-集群方案/一致性哈希 — 数据分片的基础理论