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。
三、选举触发条件
当某个哨兵认为主节点客观下线后,它会尝试发起选举。具体条件:
- 该哨兵自身状态正常,且认为主节点处于 ODOWN 状态。
- 该哨兵没有正在进行的故障转移。
- 该哨兵在最近一次投票中未投给其他候选者(或等待超时)。
- 该哨兵的
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 和投票对象。
- 如果已经投过票,则拒绝,并返回自己当前投票的
runid和epoch。
注意: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 失效,其他哨兵会重新发起选举。
五、关键细节与面试要点
-
epoch 的作用:类似于 Raft 的 term,用于保证选举的单调递增,避免旧 Leader 干扰。每个哨兵维护
current_epoch,在收到更大 epoch 的请求时会更新自己的 epoch。 -
投票的唯一性:每个哨兵在每个 epoch 中只能投一票,且投票后在该 epoch 内不再改变。这保证了不会出现两个 Leader 同时获得多数票。
-
多数派原则:必须获得超过半数的票数才能当选,这避免了脑裂。如果哨兵数量为偶数,多数派要求更严格(例如 4 个需要 3 票)。
-
随机超时:如果选举失败,Candidate 会等待一个随机时间后再次发起选举,以减少冲突。这个随机时间通常基于
SENTINEL_ELECTION_TIMEOUT计算。 -
与 Raft 的区别:
- Sentinel 选举不是持续的心跳和日志复制,而是按需触发。
- Sentinel 的投票请求复用了
is-master-down-by-addr命令,而不是独立的 RequestVote RPC。 - Sentinel 没有日志复制阶段,Leader 只负责故障转移,不负责数据一致性。
-
故障转移后的处理: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 算法流程

