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

6.9 KiB
Raw Blame History

tags, create time
tags create time
分布式
Raft
共识算法
选举
故障转移
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

详细步骤:

  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 选举 + 日志复制 任意值的共识
活跃节点要求 需要多数派存活 需要多数派存活

关联笔记