多数派的定义分布式系统里(比如 Etcd、ZooKeeper、Consul、Redis Sentinel、MongoDB ReplicaSet 等)一般采用多数派原则。
多数派原则(majority):
只有多数派节点(majority of voting nodes)同意,写入才算成功;只有多数派在线,集群才能选出主节点。少数派分区永远不能参与选举,也不能投票
选举(leader election)和一致性仲裁(quorum)更高效、更可靠。在最少节点的情况下达成最高的可用性。
多数派的定义是:
1quorum = floor(N/2) + 1
多数派选举需要 >50% 的票数
奇数节点能减少浪费的资源
偶数节点不会提高容错能力
奇数节点能避免“平票”(leader 选不出来)
奇数投票节点确保一致性?(1)避免平票,保证能选出 Primary奇数节点让投票在任何情况下都能明确区分“多数派”和“少数派”,而不会出现 50/50 的对等分裂,从而避免平票,让 leader 一定能选出来。
主节点选举依赖多数票。示例对比:
✔ 使用 3 个投票节点(奇数)!
123456节点:A, B, C (3 voting)majority = 2假设 A 挂了:剩下 B、C 仍有 2 票可继续选主
系统仍然 有主节点 → 可继续读写。
✘ 使用 4 个投票节点(偶数)
12节点:A, B, C, Dmajority = 3
即使挂掉一个:
1剩 3 个节点 → 需要 3 票 → 刚好全部在线
任何一个节点再出现网络抖动:
12只剩 2 票 < majority(3)无法选主 → 集群只读不能写
➡ 多花了一个节点却不能提升容错能力。
(2)写入确保不会产生“脑裂(split-brain)”写操作要求被多数投票节点确认才能成功:
举例:3 voting nodes → majority = 2
1primary 成功写入两个节点 → 数据安全
这样即使 primary 发生故障,另一个节点晋升为 primary 时:
它一定已经拥有最新多数写入的数据
旧主不会带着未提交的数据乱写
➡ 奇数投票节点保证:只有一个分区能达到 majority,另一个分区不能产生脏写。
(3)最优容错/资源比
建议投票节点必须为奇数(3、5、7) 是因为偶数节点没有增加容错能力,纯浪费资源。
节点数
多数派
容忍故障数
说明
3
2
1
常用最小集群
4
3
1
和 3 节点一样,但多浪费一个节点
5
3
2
容错能力提高了
➡ 所以为了不浪费资源,同样想增加容错能力时,会直接跳到 5 节点,而不是 4 节点。
primary 必须获得多数投票 → 避免选举平票
写操作必须多数节点确认 → 避免多主和脑裂
奇数节点最大化可用性,最小化资源浪费
因此 MongoDB 通过 majority 写入 + 奇数投票节点 + 单主架构 确保副本集中只有一个合法主节点,并且数据是强一致的。
主节点失联
注意:网络分裂是“触发选举”的原因
副本集的节点之间依赖心跳(heartbeat)互相确认是否“能连通”。如果 Primary 无法与多数投票节点通信,它必须自动放弃主节点身份。自动降级(step down)变成 Secondary、不参与选举、不参与投票。
否则:它写的数据无法同步给其他节点,其他节点可能已经选出新的 Primary,两个 “主” 会产生冲突写,破坏一致性。
能连通 = 存活节点
不能连通 = 故障节点
故障节点 自动被投票系统忽略。只有与大多数节点相互可达的节点才是合法的投票者。选举只发生在多数派分区中。
当主节点恢复,发现已经有新的 Primary,不会争抢主,会自动作为 Secondary 追赶 oplog(同步数据),完全不会参与正在进行的选举。
选举新主节点 Phase 1:请求成为候选人(申请 term) Phase 2:投票
在任何一个 term(选举轮次)里只能有一个候选人。
🚫 他们无法同时成为候选人(协议禁止)
🚫 electionTimeout 随机化让“同时发起”几乎不可能,即使一轮失败,也不会卡住,而是发起下一轮选举投票
🚫 即使同时尝试,term 冲突规则保证只有一个候选人存活
🚫 投票只能投给一个候选人,不能“投给自己”当候选人
Raft 用的是 随机选举超时,每个节点有一个随机 electionTimeout,这个时间到达之前,不会发起选举,所以每轮只有 1 个节点最先到时间。
即使时间因为延迟等原因差不多,Raft 规定节点只能给一个候选人投票。
B 向 C 请求投票
C 向 B 请求投票
两者收到对方的 term 后,会比较 term 大小:谁的 term 更大,另一个就自动放弃本次竞选(step down),票投给 term 更大的那一方。所以最终仍然会选出一个 Primary。这叫 term 冲突决议。
什么情况下会卡住?
多数派本身断裂:A /断网/ B C,集群仍然正常运行(在 B、C 那一边)
如果分裂成:A B | C,AB都在选自己,C又断网→无法投票,本轮失败 → 下一轮继续随机超时 → 必然选出一个。
有多数派 → 一定能选出来
没多数派 → 协议自动重试,直到选出(除非网络把多数派都断开,否则不会选不出来。)
节点故障和网络分区本质一样,但表现方式不同。
节点故障 = 无法与该节点通信 网络分区 = 一部分节点无法与另一部分通信
→ 从 Raft / Paxos / Zookeeper 的选主协议视角来看:两者都是“通信不可达”,本质等价。
1. 节点故障属于网络分区的一种特殊情况节点故障有两种典型表现:
节点宕机(进程挂了、机器断电)
节点卡死(CPU 100%,没有响应)
在这两种情况下:
其他节点发 RPC 给它
超时,一直收不到回复
效果 ≈ 这个节点被隔离到了一个别人都到不了的区域
这就是一个非常典型的 “一对多” 网络分区:
12正常区域:A、B、C隔离区域:D(单独一块)
从多数派算法看:
D 和大家无法通信
A、B、C 仍然构成多数派,继续运行
所以“节点故障”其实就是“单节点网络分区”。
2. 网络分区不一定是节点故障网络分区可以更复杂,比如:
情况 1:分成 2-2 的两个组(4 节点例子)
1A B | C D
两边互相看不到
每一边都认为另一边“宕机”
但实际上大家都还在,只是通信断了。
情况 2:单链路损坏
123A -- CA X BB -- C
A 和 B 互相到不了,但都能到 C。
这不是任何节点故障,只是链路坏了。
3. 多数派协议看的是“能否通信”,不看“为什么无法通信”对于 Raft / Paxos / ZK 而言:
情况
协议视角
等价吗
节点宕机
收不到心跳 → 不可达
和“单节点分区”完全一样
节点高负载卡死
超时 → 不可达
等价
网络 cable 掉了
不可达
等价
路由异常导致某节点看不到其他节点
不可达
等价
整个机房掉电
大部分节点不可达
等价
所以:对分布式一致性算法来说,“原因不重要”,只看通信结果。
4. 为什么要把它们视为同一种情况?因为共识算法的核心目标是:
保证只有多数派区域能够选主、写数据。
不管是因为:
节点宕机
火灾
网卡坏了
交换机掉线
延迟过高
TCP 拥塞太严重
机器卡死不回包
某段链路丢包
最终的结果都是节点之间无法通信。所以从一致性协议角度:
节点故障”和“网络分区”都是“不可达”。 它们的后果与处理方式完全一样。
拓展问题MongoDB 如何判断一个节点“失联”?(基于心跳、electionTimeout)
投票节点(voting node)和数据节点(non-voting node)的区别?
如果主节点没有 step down 会怎样?(真实脑裂案例)
“网络分裂情况下的时间线”描述一次真实的 MongoDB 选举过程
License
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 许可协议,转载请注明出处。