本文目录导读:

ZooKeeper 的 Leader 选举过程是其核心机制,用于在集群中选出一个 Leader 节点来负责处理写请求和事务,Follower 节点则负责处理读请求和投票,选举主要在 服务器启动阶段 或 Leader 故障/失联 时触发。
ZooKeeper 默认使用 FastLeaderElection 算法(基于 Paxos 和 Zab 协议),以下是选举过程的详细分解:
选举触发条件
- 服务器启动时:所有节点均处于 Looking 状态(选举中),需要选出一个 Leader。
- Leader 崩溃或网络隔离:当前 Leader 无法与集群中超过半数的节点保持连接,导致集群进入 Looking 状态。
核心选举依据:事务 ID (ZXID) 和服务器 ID (myid)
选举的关键是投票给哪个节点,比较的优先级如下:
- 比较 ZXID:最后处理的 事务 ID(zxid 是一个 64 位数字,高 32 位为 epoch(纪元),低 32 位为计数),ZXID 越大,表示数据越新。
- 比较 myid:ZXID 相同,则比较 服务器 ID(在
myid文件中配置的整数,如 1, 2, 3),myid 越大,优先级越高。
选举结果:ZooKeeper 会选择 ZXID 最大 且 myid 最大 的那个节点作为 Leader(因为 ZXID 大的节点拥有最新的数据,可以保证一致性)。
选举过程详解(以 5 台机器为例,初始启动)
发起投票
- 所有节点都认为自己没有投票,因此每一台服务器都会投自己一票。
- 初始投票信息:
(myid, ZXID),如果数据完全一致,所有服务器都会推荐自己。
接收并比较投票
-
每台服务器将投票发给集群中的其他所有服务器。
-
当服务器 B 收到服务器 A 的投票
(myid_A, ZXID_A)时,B 会将其与自己的投票(myid_B, ZXID_B)进行对比:- 规则:先比较 ZXID,选大的;ZXID 相同,再比较 myid,选大的。
- A 的优先级更高:B 会改变自己的投票,转而投给 A(即更新自己的投票为
(myid_A, ZXID_A)),并且把这个更新后的投票重新广播给所有节点。 - B 的优先级更高:B 维持原投票,并继续广播。
统计票数,确定 Leader
- 每台服务器不断接收和比较投票,并更新自己的投票。
- 一个节点会统计收到的所有投票(包括自己的),当 某台服务器(比如节点 A)得到的票数超过集群总节点数的一半(即
> n/2) 时,该服务器 A 就成为 Leader。- 例如:5 台节点,A 得到 3 票(包括自己),则 A 胜出。
- 重要:这个准则是过半原则,ZooKeeper 集群必须包含奇数台节点(如 3, 5, 7)以避免平局。
改变状态
- 赢得投票的节点:状态从
LOOKING变为LEADING,成为 Leader。 - 其他节点:状态从
LOOKING变为FOLLOWING,成为 Follower。 - Leader 会向所有 Follower 发送一个 NewLeader 通知,Follower 确认后,Leader 会发送 Uptodate 确认,至此,集群进入正常工作状态。
特殊情况处理
Leader 故障后的快速重新选举
- 假设当前 Leader 是节点 3(myid=3, ZXID 较大),当它宕机后:
- 剩余节点(如 1, 2, 4, 5)检测到 Leader 失去连接,进入
LOOKING状态。 - 它们之间互相投票,由于节点 5 拥有仅次于 Leader 3 的最大 ZXID(或许它与 3 同步了最新数据),它会获得大部分票。
- 节点 5 成为新 Leader,这个过程通常很快,因为数据差距不大。
- 剩余节点(如 1, 2, 4, 5)检测到 Leader 失去连接,进入
Leader 被孤立(脑裂防止)
- ZooKeeper 不允许“脑裂”(两个 Leader 同时存在),因为 Leader 只有在接收到超过半数节点的支持时才能继续当 Leader。
- 假设集群为 5 台机器,Leader 1 与 Follower 2,3 在一个分区,Follower 4,5 在另一个分区。
- 分区 1:Leader 1、F2、F3 可以互相通信(3 台,过半)。
- 分区 2:F4、F5 只能互相通信(2 台,不足过半)。
- 规则:分区 2 中的 F4 和 F5 无法选举出 Leader,因为它们各自只能得到 2 票(未过半数),所以它们一直处于
LOOKING状态,不会产生新 Leader,分区 1 仍可正常服务。 - 这保证了只有拥有多数节点的分区才能产生 Leader,避免了数据不一致。
关键概念总结
| 术语 | 含义 | 作用 |
|---|---|---|
| myid | 服务器唯一 ID (整数) | 写死在 data/myid 文件中,用于在 ZXID 相同时决定谁更优先 |
| ZXID | 事务 ID | 64 位整数,高 32 位是纪元(epoch),低 32 位是计数,代表了数据的新旧程度 |
| epoch | 时代/纪元 | 每次新 Leader 选出,epoch 都会加 1,用于隔离历史 Leader 发出的过时投票 |
| Quorum(法定人数) | 过半数 | > n/2,选举和提交事务都必须达到该数量,防止脑裂和数据丢失 |
| Looking | 选举中状态 | 只有处于该状态的节点才会参与投票 |
总结流程图
集群剩余 / 启动
|
所有节点:LOOKING
|
1. 投给自己:发送 (myid, ZXID)
|
2. 收到别人投票 -> 比较 (ZXID > or myid >)
| |
| +---- 别人大:更改投票为他,继续广播
| +---- 自己大:维持原票
|
3. 统计:某个节点得票数 > 总节点数/2
|
+---- 是:该节点成为 LEADING (Leader),其他变为 FOLLOWING
|
+---- 否:继续循环,直到选出 Leader 或超时