Files
cs-note/hhs/Redis/07-集群方案/Raft共识算法.md
T
2026-05-27 11:24:47 +08:00

119 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [分布式, Raft, 共识算法, 选举, 故障转移]
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 的心跳,就会发起选举:
```mermaid
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 选举简化应用到故障转移场景:
```mermaid
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
```
**详细步骤:**
1. **超时检测**:Replica 发现 Master 超过 `cluster-node-timeout` 未响应 PONG
2. **纪元递增**:Replica 将当前 epoch 加 1(类似 Raft 的 term 递增)
3. **发起投票**:向集群中所有 Master 节点发送投票请求
4. **投票规则**:每个 Master 在同一个 epoch 内**只投一票**,先到先得
5. **赢得选举**:获得多数 Master 投票的 Replica 升级为 Master
6. **广播新配置**:新 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-集群方案/一致性哈希]] — 数据分片的基础理论