想象你是一家火锅店的合伙人之一。店里共有 4 位合伙人,大家共同签署过一份《合伙协议》,约定了店里的大事(上新品、改菜单、进设备)必须至少 3 位合伙人签字才算数。有一天,老王想招 2 位新合伙人入伙。他图省事,直接在协议里添了两个名字,然后拉着另一位老合伙人说“咱们 2 个人以后说了算”。结果第二天,新老两班人各自在厨房里发号施令,一锅毛肚被煮了三遍。这个火锅店的故事,就是分布式系统里 Raft 集群成员动态变更的真实写照。Raft 集群是一组电脑节点共同维护一份数据的“合伙人团体”,它们靠投票选出一个“店长”(Leader)来拍板。当你需要给集群扩容、缩容、替换机器时,如果像老王那样直接换掉配置,就可能分裂成两个互不承认的团体,各自选出一个店长——这就是著名的“脑裂”。好在 Raft 论文给出了两套安全流程:一次只换一个成员的“单节点变更”,以及新旧两份名单共同过渡的“联合共识”(Joint Consensus)。学会这两招,你就能在不停业、不关店的前提下,安全地完成合伙人换血。

岗位关联

掌握 Raft 成员变更的安全流程,是分布式存储工程师做“不停机扩缩容”的基本功,能帮你避免线上脑裂事故,让集群升级像换一套班子一样平稳。

目录
  1. 可视化演示
  2. 成员变更问题
  3. 脑裂风险
  4. 单节点变更
  5. 联合共识
  6. 变更流程细节
  7. 工程实践建议
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

这篇讲的是Raft协议里怎么安全地在线增减集群节点成员。你会学到Joint Consensus联合共识的两阶段... 切换模式查看不同维度。

本节配有交互动效(进入视口后自动加载,约 3KB)。

成员变更的脑:核心原理 动态展示关键流程与数据流动 交互控件调节参数实时渲染 f(x) = transform(x) 核心概念的可视化呈现

成员变更问题

火锅店要开分店了。老王盘算着:得招 2 个新合伙人进来,让 1 个老合伙人退股。如果为了改《合伙协议》而停业一天,门口排队等位的老顾客全跑去隔壁烧烤店。分布式系统的日子也一样:Raft 集群正忙着服务线上请求,你却要往里面加机器、减机器,或者把一台快报废的服务器替换掉。中断服务意味着损失,最好能“热着”把名单改了。

Raft 集群里的每个成员,就是一位合伙人;全体成员的名单,在 Raft 里叫做“配置”(Configuration),通常记作 C。成员变更,本质上就是修改这份名单。运维中常见三类需求:业务量上涨要扩容加节点;硬件老旧要替换节点;机房迁移要整体换一批节点。这些操作如果靠停服来做,最简单最稳妥,但代价是服务中断;如果在线做,就必须保证新老名单交接时不出乱子。

为什么改个名单这么难?因为 Raft 的一切安全承诺都建立在“多数派”之上:选 Leader 需要多数票,日志提交需要多数确认。你每增删一个节点,多数派的边界就移动一次。如果移动过程中出现“两个多数派”,系统就会精神分裂。所以我们先定义清楚“名单”长什么样。

回到火锅店:名单就是《合伙协议》上的签名栏。谁在名单上,谁就有表决权;谁不在名单上,谁就不能拍板。接下来的所有问题都围绕一件事——如何修改这张名单而不分裂。

C={n1, n2, …, nm}\displaystyle C = \{ n_1,\ n_2,\ \dots,\ n_m \}
这个公式表示:配置 C 就是集群成员节点的集合,m 是当前节点的数量。Raft 的全部安全规则,都是围绕“集合 C 上的多数派”展开的。
  1. 成员节点$n_i$ 表示集群中第 $i$ 个节点,就像合伙人名单上的第 $i$ 位名字。
  2. 集合大括号$\{ \dots \}$ 是集合符号,把这些节点收拢成一份“合伙人名单”。
  3. 配置变量$C$ 是这份名单的代号,Raft 算法谈论的“旧配置 C_old”“新配置 C_new”都从这里来。

脑裂风险

老王直接改了协议,然后拉着关系最好的老李说:“咱俩就是新团队的代表。”不巧的是,那天店里网络出了点问题:前厅和后厨之间信号不通。前厅的 3 位老合伙人聚在一起,推举老王当店长;后厨的 2 位老合伙人和 2 位新合伙人聚在一起,推举老李当店长。两个店长同时喊“听我的”,传菜单的伙计们彻底懵了。

这就是分布式系统里最可怕的事故——脑裂。正常运行时,Raft 靠“一个任期至多一个 Leader”来保证日志一致。但当配置变更和网络分区同时发生时,如果新老配置之间没有任何重叠,旧配置的一部分节点可以凑够旧配置的多数派,新配置的一部分节点也可以凑够新配置的多数派,两个多数派就能各自选出一个 Leader。

根本原因在于:直接替换配置时,不同节点切换到新配置的时机不一致。有的节点还在用 C_old,有的已经用 C_new。C_old 里的一群节点认为“我们过半了,选老王”;C_new 里的一群节点认为“我们也过半了,选老李”。两边互不知情,各自把新菜谱写在自己的小本本上,以后菜单就对不上了。

火锅店的教训很直观:新旧名单没有共同认识的人,两边各开各的会,谁都不服谁。要避免脑裂,最核心的一条原则就是——新老配置必须“有交集”。

∃ L1≠L2:Q(V1,CA)∧Q(V2,CB)\displaystyle \exists\, L_1 \neq L_2 :\quad Q(V_1, C_A) \wedge Q(V_2, C_B)
这个公式描述脑裂条件:存在两个不同的 Leader L₁ 和 L₂,并且有一个节点集合 V₁ 在配置 C_A 中拿到多数,另一个节点集合 V₂ 在配置 C_B 中也拿到多数。
  1. 存在量词$\exists$ 读作“存在”,表示“会出现下面这种坏情况”。
  2. 两个 Leader$L_1 \neq L_2$ 表示两个不同的店长同时出现。
  3. 多数判定函数$Q(V, C)$ 约定为“节点集合 V 在配置 C 里拿到了足够票数”。
  4. 同时成立$\wedge$ 是逻辑“且”,表示两个多数派在同一个时间窗口里各自成立。

单节点变更

既然一次换一批容易出乱子,那一次只换一个合伙人呢?比如这周让老赵退股,招小周进来;下周让小钱退股,招小孙进来。每次新旧名单只差一个名字,总有几个老熟人两边的会都参加过。他们知道上一轮议过什么,也清楚下一轮要改什么,天然起着“牵线搭桥”的作用。

这个朴素的想法,就是 Raft 论文里的“单节点变更”(Single-Server Change)策略:任意时刻只增删一个节点。它的美妙之处在于,相邻两个配置 C_i 和 C_{i+1} 几乎一模一样,只差一个元素,因此两者的多数派必然有公共节点。这个公共节点既属于旧名单的多数,也属于新名单的多数,它不可能同时投票给两个不同的候选人,于是两个 Leader 就永远凑不到一起。

为什么这样能防脑裂?因为如果旧配置选出 Leader X,旧多数派里一定有这个公共节点;如果新配置选出 Leader Y,新多数派里也一定有这个公共节点。同一个节点不可能在同一个任期里投两次票——它要么投给 X,要么投给 Y。只要“每次只变一个”成立,任意两个连续配置的多数派都共享至少一个节点,脑裂在数学上就被堵死了。etcd 等生产级系统正是采用这种策略。

单节点变更就像一次翻一页地改章程:每一步都很小,每一步都安全。缺点是如果要从 3 节点扩到 9 节点,要连续操作 6 次,步骤多、耗时长。于是 Raft 论文还给出了另一种更“豪华”的方案。

Cold∩Cnew≠∅\displaystyle C_{old} \cap C_{new} \neq \varnothing
这个公式是单节点变更的安全命脉:旧配置和新配置的交集不为空,也就是新旧名单里至少有一个共同成员。
  1. 旧配置$C_{old}$ 是变更前的合伙人名单。
  2. 新配置$C_{new}$ 是变更后的合伙人名单。
  3. 交集符号$\cap$ 表示“两边都有的成员”。
  4. 非空条件$\neq \varnothing$ 表示交集至少包含一个人,这位“老熟人”就是阻止脑裂的锚点。

联合共识

遇到大场面,一次换一个太磨叽。假设火锅店要从 4 位合伙人扩到 6 位,老王想一步到位。Raft 论文于是设计了一个“过渡期双班子”方案:先把新老两班的名单合并成一份临时名单 C_old ∪ C_new,在这个临时期里,任何重大决议都必须同时获得老班子的多数和新班子的多数签字才生效。等到一切稳定了,再改回只保留新班子的名单。

这就是联合共识(Joint Consensus)。它把一次成员变更拆成两个阶段。阶段一:集群在联合配置 C_joint = C_old ∪ C_new 下运行,所有日志提交、Leader 选举都要“双多数”——既满足旧配置的多数,又满足新配置的多数。阶段二:联合配置被提交后,Leader 再提出新配置 C_new 的日志条目,一旦 C_new 被双多数确认提交,旧配置里的成员就正式退出决策。

联合共识为什么安全?因为它把“决策门槛”抬高到了同时跨越新旧两代名单。网络分区后,任何一侧想单独选出 Leader,都必须同时拿到旧名单多数和新名单多数。在我们之前的例子中,分区一侧只有 3 个节点,既凑不齐旧配置 4 人中的 3 人(差一点),也凑不齐新配置 4 人中的 3 人(因为新名单成员分散在两侧),于是两边都选不出 Leader,脑裂被天然拦截。

联合共识在理论上非常优雅,代价是实现复杂度高。因为两套配置并存时,节点要同时维护两套日志匹配规则,代码分支多、出 bug 概率高,所以多数工业系统选择了“单节点变更”。但理解联合共识,能帮你彻底明白“双多数”为什么能锁死脑裂。

Q(V,C)=∣V∩C∣≥⌊∣C∣2⌋+1,Commit  ⟺  Q(V,Cold)∧Q(V,Cnew)\displaystyle Q(V,C)=\left|V\cap C\right|\ge\left\lfloor\frac{|C|}{2}\right\rfloor+1,\qquad \text{Commit} \iff Q(V,C_{old})\wedge Q(V,C_{new})
上面两个公式分别定义了“过半”和“联合共识提交条件”:先算出每个配置的过半门槛,再要求 V 同时满足旧、新两个配置的过半门槛,才能提交配置变更日志。
  1. 过半门槛$\left\lfloor \frac{|C|}{2} \right\rfloor + 1$ 表示向下取整后加一,即“至少半数以上的一张票”。
  2. 双条件提交$\text{Commit} \iff Q(V,C_{old}) \wedge Q(V,C_{new})$ 读作“提交当且仅当旧名单过半且新名单过半”。
  3. 取交集统计$\left| V \cap C \right|$ 统计投赞成票的节点里,有多少位属于名单 C。

变更流程细节

合伙人变更不能拍脑袋。正规流程是:先由店长起草一份《章程修改案》——这就是一条新日志;然后传给每位合伙人传阅签名——这就是日志复制;等签名数够了,正式盖章公示——这就是提交生效。每一步都有顺序,跳一步就可能乱套。

Raft 里的成员变更,本质上是把“配置”当作一种特殊的状态,由 Leader 统一以日志条目(Entry)的方式追加到日志里,再复制到各个节点。配置变更不是瞬间发生在不同节点上的,它是一条条有序的日志,保证了所有节点按同样的顺序看到同一份名单变化。

完整流程如下:第一步,客户端向 Leader 发起新增/删除节点请求;第二步,Leader 将新配置(单节点变更为 C_new,联合共识为 C_joint)包装成日志条目追加到自己的日志;第三步,Leader 把这条日志并行复制给所有节点,直到满足提交条件——单节点变更只需新配置多数派,联合共识需要新旧双多数;第四步,配置日志提交后立即生效,所有节点应用新配置。这里还有两个关键约束:新加入的节点必须先追上日志进度,绝不能一进来就竞选 Leader;Leader 在提交配置日志时,自己必须在新配置中,否则会出现“新配置里没有 Leader 可用”的尴尬。

这条流程保证了“改名单”这件事本身也遵守 Raft 的日志顺序。就像火锅店改章程必须先过半数合伙人签字,配置日志也必须先过半数确认才能落地。

∣acked∩Cjoint∣≥⌊∣Cjoint∣2⌋+1\displaystyle \left|\text{acked}\cap C_{joint}\right|\ge\left\lfloor\frac{|C_{joint}|}{2}\right\rfloor+1
这个公式表示联合配置日志的提交条件:确认收到该日志的节点数,必须达到联合配置名单的过半数。
  1. 确认集合$\text{acked}$ 是已回复“收到这条配置日志”的节点集合。
  2. 联合名单$C_{joint}$ 是过渡期的新旧合并名单。
  3. 实际票数$\left|\text{acked}\cap C_{joint}\right|$ 数一数确认节点里有几位属于联合名单。
  4. 提交阈值达到 $\left\lfloor |C_{joint}|/2 \right\rfloor + 1$ 才算提交成功。

工程实践建议

连锁火锅店开分店时,总部会派督导员:先给新店做卫生检查(健康检查),然后一家一家慢慢开(串行变更),万一开业三天消防不合格就立刻停业整改(回滚)。集群扩缩容同理,操作手册应该写得像督导清单一样严谨。

生产环境中,成员变更的工程要点可以浓缩为三条。第一,逐节点操作:每次只加或删一个节点,禁止一次性批量替换;每完成一步,等该节点日志追平、稳定参与心跳后再进行下一步。第二,健康检查:添加新节点前检查它是否能正常连接集群、日志落后多少;变更过程中持续监控 Leader 是否频繁切换、选举是否超时。第三,回滚预案:如果新节点一直追不上日志,或者加入后集群不稳定,要能通过“反向变更”把它移除;配置文件和多版本保留,随时准备退回上一步。

以 etcd 为例,扩容操作通常是这样:先用 etcdctl member add 在集群里登记新成员,再启动新 etcd 进程并指向已有集群,观察新成员日志追赶进度,最后确认集群健康后再继续加下一个。缩容时先移除节点再下线机器,避免下线过程影响多数派存活性。连续多次单节点变更之所以整体安全,靠的是每一步相邻配置都有交集这条链式保证——整个变更过程始终有“共同成员”锚定大局。

回到火锅店:督导清单上的每条检查项,对应着集群运维的每个安全阀。把“逐节点、先体检、可回滚”这九个字刻在工位上,线上扩缩容就能像开分店一样稳妥。

∀ i∈[1,k−1]:Ci∩Ci+1≠∅\displaystyle \forall\, i \in [1, k-1]:\quad C_i \cap C_{i+1} \neq \varnothing
这个公式表示连续 k 次单节点变更中,任意相邻两次配置的交集都不为空,保证整条变更链始终有公共成员。
  1. 任意步骤$\forall\, i$ 表示对变更过程中的每一步 i 都成立。
  2. 区间范围$i \in [1, k-1]$ 覆盖从第一次到最后一次相邻交接。
  3. 相邻交集$C_i \cap C_{i+1} \neq \varnothing$ 保证每一步都有一位“老熟人”在场。

AI 应用场景

云原生智能运维中的 etcd 集群扩缩容

阶段:智能运维与基础设施容量管理

解决的问题:AI 平台的模型注册中心、调度状态和配置都存放在 etcd 这类分布式键值存储中。传统做法扩容 etcd 需要停服维护,而 AI 业务 7×24 小时推理不能中断;直接改配置又可能因为脑裂导致调度数据错乱。利用 Raft 成员变更流程,可以在不打断模型推理服务的前提下平滑扩缩容。

具体案例:某公司为 AI 训练平台扩容 GPU 节点池时,后端 etcd 集群需要从 3 节点扩到 5 节点。运维同学采用“逐节点变更”方案:先用 etcdctl member add 登记第 4 个成员,启动新节点并等待日志追平,确认集群健康后再加入第 5 个节点,全程监控 Leader 稳定性,零停机完成扩容。

数据机房迁移时的新旧集群平滑过渡

阶段:数据迁移与容灾演练

解决的问题:旧机房要整体搬迁,承载 AI 特征存储的 Raft 集群需要从 A 机房迁移到 B 机房。一次性切换 IP 会造成新旧节点互不相识,脑裂风险极高。使用联合共识的思路,把新机房节点逐步加入集群,形成“新旧两班人马共同值班”的过渡期,全部就位后逐步下线旧节点。

具体案例:某推荐系统团队把 5 节点 Raft 集群从老机房迁到新机房。他们先在新机房部署 2 个节点并加入集群,随后再部署 2 个节点,等新旧 8 个节点共同运行并同步完所有日志后,再一个个下线旧机房节点,最终新机房 5 节点完全接管。

故障节点自动摘除与自愈

阶段:异常检测与自动化故障恢复

解决的问题:AI 训练集群中某个存储节点反复宕机,传统人工修改配置容易误操作。通过健康检查机制自动发现失联节点,并触发 Raft 成员变更将其安全移除,同时将日志迁移到备用节点。

具体案例:某自动驾驶数据平台中,一个存储节点因硬件故障反复重启。巡检系统连续 5 次心跳超时后自动触发“剔除节点”流程,先确认其余节点日志完整,再执行单节点移除,整个过程服务无感知。

Python 演示

python55 行
"""Raft 成员变更安全判定模拟器: 联合共识双多数 vs 直接切换脑裂对比"""class JointConsensus:    """联合共识配置: 同时持有旧、新两份名单, 决策必须双多数通过"""     def __init__(self, c_old, c_new):        self.c_old = set(c_old)        self.c_new = set(c_new)        self.joint = self.c_old | self.c_new     @staticmethod    def quorum(config):        return len(config) // 2 + 1     def old_quorum(self):        return self.quorum(self.c_old)     def new_quorum(self):        return self.quorum(self.c_new)     def can_commit(self, granted):        ok_old = len(granted & self.c_old) >= self.old_quorum()        ok_new = len(granted & self.c_new) >= self.new_quorum()        return ok_old and ok_new     def can_direct_leader(self, granted):        """直接切换配置时, 只要旧名单或新名单任一多数即可选主"""        return len(granted & self.c_old) >= self.old_quorum() or \               len(granted & self.c_new) >= self.new_quorum()     def describe(self, name, granted):        joint_ok = "通过" if self.can_commit(granted) else "否决"        direct_ok = "能选Leader" if self.can_direct_leader(granted) else "不能选Leader"        return ("%s: 旧票=%d/%d 新票=%d/%d | 联合共识=%s 直接切换=%s"                % (name,                   len(granted & self.c_old), self.old_quorum(),                   len(granted & self.c_new), self.new_quorum(),                   joint_ok, direct_ok))  def simulate_partition():    jc = JointConsensus(c_old=["A", "B", "C", "D"],                        c_new=["C", "D", "E", "F"])    print("== 网络分区: {A,B,C} 与 {D,E,F} 互不相通 ==")    print(jc.describe("分区1 A/B/C", {"A", "B", "C"}))    print(jc.describe("分区2 D/E/F", {"D", "E", "F"}))    print()    print("结论: 直接切换会脑裂; 联合共识两侧都凑不齐双多数, 安全")    print()    print("== 正常情况: 6 节点全部在线 ==")    print(jc.describe("全部在线", {"A", "B", "C", "D", "E", "F"}))    print("联合配置提交后切换到 C_new, A、B 退出")  if __name__ == "__main__":    simulate_partition()
目的:模拟网络分区下“直接切换配置”和“联合共识”两种方案的选举结果,直观展示双多数机制如何阻止脑裂。
  1. self.c_old = set(c_old)把旧节点名单转成集合,方便计算交集和投票数。
  2. def quorum(config)计算一个配置的过半门槛,对应公式 floor(N/2)+1。
  3. ok_old and ok_new联合共识要求新旧两个多数派同时满足,这是防脑裂的核心。
  4. can_direct_leader模拟“直接切换”时的宽松选主条件,只要任一配置过半数就能选出 Leader。
  5. describe同时打印联合共识判定和直接切换判定,方便对比两种方案的差异。

常见误区

误区改集群成员不就是编辑一下配置文件,然后逐台重启吗?最多停几分钟服务,反正数据不会丢。

正解这种“土办法”风险极高。重启过程中各节点进入新配置的时机不同,一旦碰上网络抖动,旧配置节点可能选出一个 Leader,新配置节点又选出另一个 Leader,数据立刻分叉。Raft 要求配置变更必须作为日志条目按顺序复制,绝不能靠手动改文件“各改各的”。

为何常见:单机系统的直觉是“配置文件改了重启就生效”,但分布式系统里每个节点看到的世界必须一致,手动改文件无法保证这种一致性。

误区新节点性能好配置高,加进集群后应该立刻让它当 Leader,这样能提升集群性能。

正解新节点刚加入时日志几乎是空的,而 Raft 的选举要求候选人日志至少不落后于多数节点,否则它当选后会把已提交的日志覆盖掉,这是致命的。正确做法是先让新节点以 Follower 身份追日志,等它的日志追平后再参与竞选。

为何常见:人们习惯用“新机器更强”的直觉推断分布式角色,忽略了 Raft 日志匹配的硬性安全约束。

误区反正新节点会从 Leader 同步全部数据,干脆一次性把 5 个旧节点全换成 8 个新节点,一步到位省得麻烦。

正解一次性替换全体节点意味着 C_old 和 C_new 交集为空,新旧配置的多数派可以完全互不相交。配合网络分区,就会复现两个多数派各自选主、互相覆盖日志的脑裂灾难。无论用单节点变更还是联合共识,核心都是保证新老配置“有交集”。

为何常见:直觉上“全换新”很彻底,但分布式安全讲究的是“新旧交替必须有重叠锚点”,这与直觉相反。

误区Raft 论文里写了联合共识,那生产环境当然应该优先用联合共识,一步到位多换几个节点。

正解联合共识理论完备,但实现复杂度高:节点要同时维护两套配置的多数派判定、任期和投票规则,容易写出隐蔽 bug。etcd 等成熟系统实际采用单节点变更,把复杂度转移为“多操作几步”。选型时应优先考虑工程可靠性,而不是理论最优。

为何常见:论文方案容易让人趋之若鹜,但工程上“足够简单且被验证”往往比“理论完美”更重要。

误区配置是元数据,跟业务日志是两码事,Leader 直接广播给其他节点“我要加一个人”就完事了。

正解在 Raft 里,配置也是状态的一部分。如果配置变更不走日志,各节点看到配置的先后顺序就不一致,提交判定和选举规则就会错乱。配置变更必须由 Leader 追加成日志条目、复制到多数派、提交后应用,这样才能保证所有节点在同一个日志位置上达成一致。

为何常见:把“配置”想当然地当成独立于日志的外部信息,忽略了它在 Raft 协议中其实是日志的一部分。

面试题

Q1. Raft 集群成员变更时,新配置信息应该通过什么方式传递给所有节点?

单选题 · easy

  • A.配置中心远程推送
  • B.由每个节点手动修改
  • C.作为日志条目由 Leader 复制← 正确答案
  • D.通过客户端连接通知

答案:C。配置也是状态,必须作为日志条目由 Leader 统一追加并复制到各节点,保证所有节点按相同顺序看到配置变化。A、B、D 都无法保证配置传播的顺序一致性,容易造成节点间配置不一。

Q2. 联合共识在阶段一要求什么条件才能提交配置日志?

单选题 · mid

  • A.只需旧配置多数派同意
  • B.只需新配置多数派同意
  • C.旧新两配置多数派都同意← 正确答案
  • D.需要全体节点一致同意

答案:C。联合共识阶段一采用双多数规则:既要求旧配置多数,又要求新配置多数。A、B 都只满足一侧,D 把门槛抬得过高不切实际,C 才是论文定义的提交条件。

Q3. 单节点一次只变更一个成员,为什么能防止网络分区导致脑裂?

单选题 · mid

  • A.新老配置多数派总有交集← 正确答案
  • B.节点数量少所以不会分区
  • C.Leader 本身有最高票数
  • D.日志条目自动覆盖旧配置

答案:A。单节点变更时 C_old 和 C_new 只差一个元素,两个配置的多数派必然共享至少一个节点。这个公共节点不可能同时投给两个不同候选人,所以双 Leader 无法同时产生。B 与节点数量无关,C、D 都不是防脑裂的数学依据。

Q4. 新加入节点刚进集群时,为什么原则上不能立即当选 Leader?

单选题 · mid

  • A.它的地址绑定旧的 IP 地址
  • B.它尚未追平全部日志记录← 正确答案
  • C.它没有对外提供客户端端口
  • D.它必须等待其他节点授权

答案:B。Raft 选举要求候选人的日志至少不落后于多数节点,新节点日志为空,一旦当选会用空日志覆盖已提交数据。所以它必须先以 Follower 身份追日志。A、C、D 都不是 Raft 协议中的选举约束。

Q5. 以下哪些措施有助于安全完成 Raft 成员变更?

多选题 · hard

  • A.严格串行逐节点操作← 正确答案
  • B.一次替换全部老节点
  • C.新节点先追日志再投票← 正确答案
  • D.变更前做好备份与回滚← 正确答案

答案:ACD。逐节点操作保证相邻配置有交集,新节点追平日志才能安全参与选举,备份回滚提供容错兜底。B 一次性替换全部节点会破坏新旧配置交集,是脑裂高危操作,应坚决避免。

Q6. 联合共识阶段二完成后,最终生效的配置是?

单选题 · mid

  • A.新旧两份配置同时生效
  • B.只有旧配置继续发挥作用
  • C.只有新配置继续发挥作用← 正确答案
  • D.由每个节点自行投票决定

答案:C。阶段二提交 C_new 后,联合配置 C_old ∪ C_new 停止使用,旧配置成员退出,只剩新配置 C_new 继续运转。A 是阶段一的状态,B 方向反了,D 不符合 Raft 集中式配置切换原则。

Q7. 多数派选主阈值 N/2+1 的含义是?

单选题 · easy

  • A.超过半数节点才算多数← 正确答案
  • B.恰好一半节点构成多数
  • C.任意一票即可成为多数
  • D.全部节点才能构成多数

答案:A。N/2 向下取整后加 1,正好是“半数以上”。比如 4 节点需要 3 票,5 节点需要 3 票。B 的“恰好一半”在偶数场景不足,C、D 明显不符合多数派定义。

Q8. 哪些情况容易让老配置构成两个多数派,形成脑裂?

多选题 · hard

  • A.新旧配置没有任何重叠← 正确答案
  • B.新旧配置多数派有交集
  • C.网络分区且两侧都能凑票← 正确答案
  • D.配置变更未走日志复制← 正确答案

答案:ACD。新旧配置无交集时两个多数派可以完全分离;网络分区使两侧各自凑票;配置不通过日志统一传播则各节点配置不一致。B 中“有交集”恰恰是防脑裂的关键,不会导致脑裂。

Q9. 关于 Raft 成员变更,下列哪项说法正确?

单选题 · easy

  • A.变更要求集群必须停止服务
  • B.变更须以日志条目方式传播← 正确答案
  • C.变更不需要多数派节点批准
  • D.变更后旧节点全部立即下线

答案:B。Raft 成员变更的设计目标就是在线不停服,且必须作为日志条目复制并提交。A 与目标相反,C 违反了日志提交规则,D 中旧节点在阶段二逐步退出,并非“立即全部下线”。

Q10. 生产环境中部署 Raft 扩缩容,通常采用哪些实践?

多选题 · mid

  • A.逐节点串行变更← 正确答案
  • B.变更前后进行健康检查← 正确答案
  • C.失败时支持回滚操作← 正确答案
  • D.直接修改全部节点配置

答案:ABC。生产实践强调小步快跑:逐节点串行、全程健康检查、失败回滚。D 直接修改全部节点配置会引入配置不一致和脑裂风险,是典型错误做法。