Redis 哨兵选举 Leader 的 Raft 算法流程

Redis Sentinel(哨兵)是 Redis 的高可用性解决方案,负责监控主从节点、自动故障转移以及通知客户端。在故障转移过程中,多个哨兵需要选出一个 Leader 来执行实际的故障转移操作。这个选举过程借鉴了 Raft 算法中的领导者选举机制。下面详细解析其流程。

一、为什么需要 Sentinel Leader 选举?

Redis 通常部署多个哨兵节点(一般推荐奇数个,如 3 个、5 个),它们共同监控同一组主从实例。当主节点被判定为客观下线(ODOWN)后,需要从哨兵中选出一个 Leader,由它负责挑选新的主节点、执行主从切换,并通知其他哨兵和客户端。如果没有选举机制,多个哨兵可能同时尝试故障转移,导致数据不一致或脑裂。因此,Sentinel 使用类似 Raft 的选举算法来确保只有一个 Leader 执行故障转移。

二、Raft 算法在 Sentinel 中的映射

Raft 算法中,节点有三种状态:Follower、Candidate、Leader。Sentinel 选举过程中,每个哨兵节点也扮演类似的角色:

  • Follower:默认状态,等待选举或心跳。
  • Candidate:发起选举,向其他哨兵拉票。
  • Leader:获得多数票,执行故障转移。

与标准 Raft 不同的是,Sentinel 的选举不是持续进行的,而是仅在需要故障转移时触发。此外,Sentinel 使用 Raft 的“任期”(term)概念,称为 epoch(纪元),每次选举会递增 epoch。

三、选举触发条件

当某个哨兵认为主节点客观下线后,它会尝试发起选举。具体条件:

  1. 该哨兵自身状态正常,且认为主节点处于 ODOWN 状态。
  2. 该哨兵没有正在进行的故障转移。
  3. 该哨兵在最近一次投票中未投给其他候选者(或等待超时)。
  4. 该哨兵的 current_epoch 不小于其他哨兵的 epoch。

满足条件后,该哨兵成为 Candidate,开始拉票。

四、选举流程详解

1. 递增 epoch 并投票给自己

Candidate 首先将自身的 current_epoch 加 1,然后向所有其他哨兵发送 SENTINEL is-master-down-by-addr 命令,其中携带自己的 runid(运行 ID)和新的 epoch。这个命令原本用于询问主节点是否下线,但在选举中,runid 字段被用来表示“我请求你投票给我”。

Candidate 同时给自己投一票。

2. 其他哨兵的处理逻辑

每个收到请求的哨兵(Follower)会检查以下条件:

  • 请求中的 epoch 是否大于等于自己当前的 epoch?如果是,则更新自己的 epoch 为请求中的值。
  • 在当前 epoch 中,自己是否已经投过票?如果没有,则投票给请求者,并记录下这个 epoch 和投票对象。
  • 如果已经投过票,则拒绝,并返回自己当前投票的 runidepoch

注意:Sentinel 的投票是每个 epoch 只能投一次,遵循“先到先得”原则。

3. 统计选票

Candidate 收集其他哨兵的回复。如果收到的票数(包括自己的一票)满足以下条件之一,则当选 Leader:

  • 多数票:票数 >= (N/2 + 1),其中 N 是哨兵总数(包括自己)。例如 3 个哨兵需要 2 票,5 个需要 3 票。
  • 或者:如果 Candidate 发现自己的 epoch 已经领先,且获得了超过半数的票,则立即成为 Leader。

如果未达到多数票,则选举失败。Candidate 会等待一段随机时间(通常是 SENTINEL_ELECTION_TIMEOUT,默认 10 秒?实际是 sentinel.conf 中的 sentinel election-timeout,默认 10 秒?更准确的是 SENTINEL_ELECTION_TIMEOUT 为 10 秒?在 Redis 源码中,选举超时是 SENTINEL_ELECTION_TIMEOUT 定义为 10 秒?实际上,Sentinel 的选举超时是 SENTINEL_ELECTION_TIMEOUT 宏,值为 10000 毫秒,即 10 秒。但为了快速选举,Candidate 会等待一个随机延迟(SENTINEL_ELECTION_TIMEOUT 的随机比例)后重试。

4. 选举成功后的操作

一旦某个哨兵成为 Leader,它会:

  • 增加自己的 leader_epoch
  • 开始执行故障转移:从所有从节点中选出一个新的主节点(基于优先级、复制偏移量、runid 等)。
  • 向其他哨兵发送 SENTINEL failover-auth 等命令,告知自己已成为 Leader,并协调故障转移。
  • 其他哨兵收到消息后,会更新自己的 leader_epoch,并认可该 Leader。

如果在故障转移过程中 Leader 失效,其他哨兵会重新发起选举。

五、关键细节与面试要点

  1. epoch 的作用:类似于 Raft 的 term,用于保证选举的单调递增,避免旧 Leader 干扰。每个哨兵维护 current_epoch,在收到更大 epoch 的请求时会更新自己的 epoch。

  2. 投票的唯一性:每个哨兵在每个 epoch 中只能投一票,且投票后在该 epoch 内不再改变。这保证了不会出现两个 Leader 同时获得多数票。

  3. 多数派原则:必须获得超过半数的票数才能当选,这避免了脑裂。如果哨兵数量为偶数,多数派要求更严格(例如 4 个需要 3 票)。

  4. 随机超时:如果选举失败,Candidate 会等待一个随机时间后再次发起选举,以减少冲突。这个随机时间通常基于 SENTINEL_ELECTION_TIMEOUT 计算。

  5. 与 Raft 的区别

    • Sentinel 选举不是持续的心跳和日志复制,而是按需触发。
    • Sentinel 的投票请求复用了 is-master-down-by-addr 命令,而不是独立的 RequestVote RPC。
    • Sentinel 没有日志复制阶段,Leader 只负责故障转移,不负责数据一致性。
  6. 故障转移后的处理:Leader 完成故障转移后,会广播新的配置。其他哨兵更新配置,并可能重置自己的 epoch 和投票状态。

六、常见面试问题

  • :Sentinel 选举 Leader 时,如果两个 Candidate 同时拉票会怎样?
    :由于每个哨兵在每个 epoch 只能投一票,且 epoch 不同,最终只有一个 Candidate 能获得多数票。如果票数分散,则选举失败,等待随机超时后重试。

  • :为什么 Sentinel 需要多数票而不是简单多数?
    :多数票确保只有一个 Leader 能获得足够支持,避免多个 Leader 同时执行故障转移导致数据不一致。

  • :Sentinel 的 epoch 和 Raft 的 term 有何异同?
    :两者都是单调递增的逻辑时钟,用于标识选举轮次。但 Sentinel 的 epoch 还用于标识配置版本,且选举仅在故障转移时发生。

  • :如果 Sentinel 集群中有节点故障,选举还能进行吗?
    :只要剩余节点数量达到多数派(例如 3 个中剩 2 个),选举仍可进行。如果少于多数派,则无法选举出 Leader,故障转移会失败。

七、总结

Redis Sentinel 的 Leader 选举流程是 Raft 算法的一个简化实现,核心在于 epoch 递增、投票唯一性和多数派原则。理解这一流程不仅有助于面试,更能帮助运维人员正确配置哨兵集群,避免脑裂和选举失败。在实际部署中,建议使用奇数个哨兵(如 3 个或 5 个),并确保网络稳定,以减少选举超时和冲突。通过掌握这些细节,你就能在面试中从容应对 Sentinel 选举相关的问题。

未经允许不得转载:任鹏个人博客 » Redis 哨兵选举 Leader 的 Raft 算法流程

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏