☰
三节点集群挂1台没事、挂2台就瘫痪?多数派机制讲透
2026/10/21 3:55:14 网站建设 项目流程

凌晨两点多,手机在床头柜上连环震了半分钟。打开监控一看,三节点集群里两个节点标红,业务侧已经开始往用户面前弹 503。同事的第一反应是"还有一台活着啊,怎么就不能先顶着?"——这几乎是每个刚接触分布式集群的人都会问的问题。上周这套集群也挂过一台机器,剩下两台自动完成了角色接管,线上几乎无感知;这周挂了两台,剩下那一台却彻底"撂挑子"了。表面看都是"还有机器在跑",为什么待遇完全不同?

这篇文章我想把"3 台机器挂 1 台能用、挂 2 台就瘫痪"这个现象彻底讲透:它不是一个故障,而是一个设计。理解了背后的多数派投票、脑裂预防和 CAP 权衡之后,你会真正看清高可用集群的边界在哪里,也会理解容量规划时为什么总是推荐奇数节点。无论你是运维、后端开发还是正在搭个人小集群,这篇文章都值得看完。

1. 先确认场景:什么样的"3 台机器"会出现这个现象

1.1 共识型集群才是题目的主角

"3 台机器挂 2 台会瘫痪"并不是所有架构都成立。比如纯主从复制的数据库三节点,情况要复杂得多:如果挂掉的是两台从库、主库还活着,读写基本不受影响;如果挂掉的是主库,剩下两个从库往往需要人工确认数据一致性后才能提升,而不是自动恢复。所以标题里的现象最典型的场景是——三节点共识型集群。

这类系统需要一个稳定且唯一的"领导"来对外提供写服务,常见的有 etcd、ZooKeeper、Consul,以及各种基于 Raft 协议自研的中间件和分布式存储。它们的共同点非常明确:任何写操作必须经过多数节点确认,任何新领导必须由多数节点选举产生。你可以把这类系统理解为"团队决策制":不是某台机器说了算,而是大家投票,票数不够就什么都不干。

1.2 主从复制和共识集群的容错逻辑不一样

主从复制的思路是"复制数据,听主库的"。主库挂了要选新主,此时如果连一个能拍板的人都找不到,系统就悬而未决;共识集群的思路是"没有多数票,谁也不许拍板"。两种设计对"挂 2 台"的处理完全不同。

架构类型挂 1 台挂 2 台约定来源
主从复制(1 主 2 从)主在则正常,主不在则尝试选主主在可读,写在主挂后基本不可用以"主库"为权威
共识集群(3 节点)自动完成选主和数据同步整个集群保护性停摆以"多数派"为权威

如果你在生产环境观察到的现象是"挂 1 台没事、挂 2 台全挂",那你面对的九成是后者。这也是为什么共识型组件经常被用作配置中心、注册中心和分布式锁的底座——它们对一致性要求极高,宁可停机也不能出错。

2. 多数派投票:为什么剩下 2 台仍然拥有"合法席位"

2.1 Quorum 的数学底线:3 个节点里 2 个就是多数

共识算法的核心是 Quorum(法定多数)。3 节点集群,多数 = 2。规则很简单:选主需要 2 票,日志提交需要 2 个副本确认(leader 自己算一票)。用开会的例子讲:3 位合伙人约定"至少 2 人到场才能做决定"。走 1 个人,剩下 2 个人开会,所有决策照常有效;走 2 个人,剩下 1 个人,不管他喊得多大声,决议都无效。

关键在于"多数"为什么能保证安全——任意两个多数集合一定有交集。3 节点中两个人凑成一组,和另外两个人凑成一组,必然有一个人同时在场。这个"交集者"保证了:新的决策团队一定知道旧团队之前决定了什么。对应到 Raft 协议里,就是新选出来的 leader 一定持有已经被提交的日志。这个交集性质是整个共识机制安全性的基石,它从数学上杜绝了两个互不知道对方存在的团队各自做出决策的可能。

2.2 挂 1 台后的完整工作链路

我拆一下挂 1 台后集群到底经历了什么,这样你就知道"还能用"不是靠运气:

  1. 心跳超时:其中一个节点失联,剩余两台互相还能通信。
  2. 选举触发:其中一个节点发现收不到 leader 心跳,进入候选状态。
  3. 拉票:候选节点把自己日志的任期号和索引广播给其余节点。
  4. 选举成功:至少获得 2 票(自己 + 另一台),新 leader 被确认。
  5. 日志接续:新 leader 把自己身上最新日志同步给存活的 follower。
  6. 对外服务:写请求正常走完 "日志复制 -> 多数确认 -> 提交" 的流程。

整个过程对客户端是透明的,只是比平时慢几十到几百毫秒。所以"挂 1 台还能用"背后不是侥幸,而是一套完整的容错流程在几十秒内自动完成。

2.3 为什么不用 2 台、也不用 4 台

很多人都会疑惑:既然要冗余,怎么不直接上 4 台?这里有一个反直觉的数学事实。

  • 2 台:多数需要 2 票,挂 1 台就只剩 1 票,无法选举,所以 2 节点集群的容错是 0。
  • 3 台:多数需要 2 票,挂 1 台剩 2 票,继续工作;挂 2 台剩 1 票,停摆。容错上限是 1。
  • 4 台:多数需要 3 票,挂 1 台剩 3 票,继续工作;挂 2 台剩 2 票,不足 3,停摆。容错上限依然是 1。
  • 5 台:多数需要 3 票,挂 2 台剩 3 票,才能容忍 2 台故障。

规律是:N 个投票节点最多容忍 floor((N-1)/2) 个节点故障。这就是"容错能力不随机器数线性增长"的根本原因——3 台和 4 台没有区别,想多容忍一台,直接跳到 5 台。4 节点集群不仅没有提升可用性,反而多了一台机器的硬件成本和运维成本,还可能在网络分区时出现 2 对 2 的僵持局面,所以生产上很少推荐。

3. 挂 2 台的"瘫痪"是一种自我保护:脑裂与 CAP 的代价

3.1 幸存的那台机器无法证明自己是"幸存者"

挂 2 台后,剩下那一台最困惑的地方在于:它根本没法判断另外两台是"真的死了",还是"只是和它网络隔离了"。假设三台机器之间的网络出现分区,A 和 B 在一个分区,C 在另一个分区——从 C 的视角看,它也看不到 B 和 A,C 会觉得另外两台都挂了。如果系统允许 C 继续接受写请求,A/B 分区同样会认为"我们才是那个合法的多数派",两边同时接受写请求,数据立刻分叉。等网络恢复,两边的数据已经产生大量冲突,甚至出现覆盖丢失。

大家生活里有类似的场景:三个人约好"必须两人同意才能动公款"。其中两个人突然失去联系,剩下的一个人擅自买了一堆东西,等另外两个人回来发现钱已经被动了,谁能说清这是团队决定还是个人行为?规则不要求你"好心",规则要求你在人不够的时候什么都不做——这样即使另一方也在做决定,至少能保证不会出现两个互相矛盾的团队决策。三节点共识集群挂 2 台后停摆,本质是在执行这个"宁缺毋滥"的原则。

3.2 CP 优先的系统为什么宁可拒绝服务

分布式系统里的 CAP 定理说,发生分区(P)时,一致性和可用性只能选一个。三节点共识集群选择了前者:当它无法凑齐多数时,宁可拒绝所有写请求,也绝不写出一个可能被推翻的数据。对你的业务来说,后果是 503、超时、锁失效;对数据来说,后果是零。后者远比前者重要。

配置中心存着服务路由,分布式锁协调着秒杀流量,如果这些数据出现双写冲突,恢复成本和业务损失是不可估量的。所以"瘫痪"其实是一种诚实的自我保护:我现在没有能力向你保证正确性,所以我拒绝假装正常。理解这一层之后,你在凌晨看到 P0 告警时心态都会不一样——系统不是挂给你看的,它是在用停摆保护你最值钱的东西:数据的正确性。

3.3 读请求还能不能返回:不同系统的差别

很多人以为挂 2 台后读请求还能用,毕竟还剩一台机器能查本地数据。实际上要看一致性级别,这里差异很大:

系统/模式挂 2 台后的读行为说明
ZooKeeper(连接幸存节点)可能返回数据follower 可提供未必最新的读,写必然失败
etcd/Consul(线性化读)报错默认读也要过 Quorum,挂 2 台后读也停
串行化读模式可能返回旧数据不走 Quorum,但读到的是历史快照

生产环境里我见过最尴尬的一种情况:某系统的读请求配置成串行化读,挂 2 台后还能返回旧数据,监控看起来"好像恢复了一点",但写请求全挂。如果业务方不清楚这个机制,很容易拿着旧数据做决策。所以读降级不是免费的午餐,使用前必须让业务方明确知道"数据可能不是最新的"。

4. 如果真的想"挂 2 台还能用",有哪些现实方案

4.1 AP 系统:用一致性换可用性

如果你的业务可以接受短暂读到旧数据、最终一致,那就别用强一致共识集群,改用 AP 风格的分布式存储。以 Cassandra 的 NWR 模型为例:3 副本,正常配置 W=2、R=2,挂 2 台后会写失败;如果你把 W 降到 1,那么只要还剩 1 个副本,写就能成功,读设置 R=1 也能成功。

代价是什么?写入的数据可能在后台对账中被其他副本的副本数据覆盖,也可能读到旧版本,或者最后一次写入者获胜导致前一次写被吞掉。这里没有银弹,关键看业务能不能接受"我写成功了,但过一会儿数据可能不是我以为的那个值"这种语义。适合 AP 的场景包括:商品浏览计数、点赞数、会话缓存等非关键写路径;绝不适合:账户余额、分布式锁、配置更新。

4.2 加"仲裁者":从 3 个投票者变成 5 个投票者

如果数据必须存在 3 台机器上做冗余,又想容忍挂 2 台,数学上有一个解法:增加不存数据的投票者(witness / observer)。Raft 的投票者总量需要满足 2f+1,想容忍 f=2 个故障,投票者总数必须到 5。所以你可以部署 3 个数据节点 + 2 个仲裁节点,虽然还是只有 3 份数据,但投票池变成 5。挂掉任意 2 个投票者后,剩余 3 票仍构成多数,系统可以继续工作。

注意,仲裁节点通常很轻量,一台小机器就能跑,但它也不能太随意——如果仲裁者和唯一幸存的数据节点不在同一个网络分区,系统依然无法选主。而且数据只有 1 份时其实处于"裸奔"状态,这个存储节点要是再挂一次,就是真正意义上的数据全丢。仲裁方案适合"短期撑住"的场景,不适合长期让系统以单副本状态运行。

4.3 降级为只读:不给承诺,但别全停

很多业务场景(配置读取、指标查询)读多写少,挂 2 台后并不需要"完整可用",只需要"还能读到配置"。折中方案是把幸存节点切换为只读模式,拒绝写请求,但允许读本地已经持久化的数据。这个方案在一些可控的工程场景里成立,前提是业务方明确知道数据可能不是最新的。

我有一次在内部系统上试过类似方案:三个节点全部进入保护性只读,监控大屏还能展示历史指标,但任何写入操作都明确返回"集群降级中"。这相当于把系统的可用性从"强一致读写"降级为"最后一口数据还能看",比全量 503 体面得多。但别高估它——只读降级救不了写密集的业务,它只能给运维争取一点排查和恢复的时间。

5. 运维层面,这个特性逼着你想清楚的事

5.1 挂 1 台不是"没事",而是"冗余耗尽"

挂 1 台时集群还在工作,这是最容易让人麻痹的地方。此时集群已经没有任何冗余:剩余 2 台任何一个抖动、重启或网络闪断,系统立刻进入保护性停摆。我见过不少线上事故都是这个节奏——第一台节点因为磁盘问题被踢出,值班同学觉得"反正还能用",第二天第二台节点因为内存问题重启,整个平台上线直接不可用。

所以规范的做法是:挂 1 台立即告警(P2 级别),值班同学马上着手修复或替换,而不是等着自然恢复。你修复得越快,脆弱窗口就越短。很多人问"挂 1 台之后为什么还要那么紧张",答案就在这里:你已经从"容错 1"变成了"容错 0"。

5.2 告警策略和演练:把"恢复节点"变成肌肉记忆

针对三节点集群,监控至少要覆盖这几项:存活节点数、leader 是否存在、心跳延迟、日志复制延迟。告警分级可以参考这个思路:

集群状态告警级别响应要求
挂 1 台P2,10 分钟未恢复升 P1值班同学立即介入
挂 2 台P0全组响应,立刻恢复节点
leader 频繁切换P1排查网络抖动、磁盘 IO、时间偏差

另外强烈建议定期做"拔节点演练"——物理断开一台机器的网络,观察集群是否自动完成角色切换;再拔第二台,验证保护性停摆是否符合预期。这个过程能暴露很多平时发现不了的问题:某节点持久化目录权限异常、选举超时配置不合理、某台机器时钟偏差过大。演练不是走形式,它是在给自己积累"真出事了知道先看哪里"的肌肉记忆。

5.3 容量规划:3、4、5 台到底怎么选

最后是选型建议。绝大多数业务,三节点共识集群是性价比最高的:一份数据冗余在 3 台,容忍 1 台故障,选主和日志复制算法成熟。如果预算充足、业务属于关键链路,可以上 5 节点,容 2 故障。4 节点不建议:它和 3 节点的容错上限一样是 1,但多了一台机器的维护成本,还可能在两两分区时陷入僵持。

如果业务真的要求"3 台机器挂 2 台还能正常服务",那已经不是"加一台机器"能解决的事了——你需要的是 5 个投票者,或者把业务改造成 AP 化,再或者做好只读降级预案。搞清楚了这些边界,你在下一次开会讨论架构时就能直接说出结论:要容几台故障,就给几个投票者,别的都是心理安慰。

在真实运维里,我的体会是:高可用从来不是"机器多"堆出来的,而是"多数派"这条规则划出来的。理解了这个边界,你就不会在挂 1 台时掉以轻心,也不会在挂 2 台时对着监控骂系统"明明还有一台活着却不肯干活"——它不是在罢工,它只是在用停摆保护你最值钱的东西。遇到这类问题,先看存活节点数是否达到多数,再看是否有 leader,最后看日志复制延迟,绝大多数故障在十分钟内都能定位。希望这篇能把"为什么 3 台挂 2 台就瘫痪"这件事讲清楚,下次再有人问你,直接把这篇文章丢给他就行。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询