一、七个长老,一件事也定不下来

青山镇有个议事厅,厅里坐着七位长老。镇上大小事务——修桥、办学、分粮、招商——都得七位长老一起议、一起定。

长老们都是好人,但不代表他们不吵架。

"南街的桥该修了。"赵长老说。

"修北街的,北街的人多。"钱长老说。

"我两个都不同意,"孙长老说,"今年的银子不够修两座,不如先补学堂的瓦。"

七个人,七张嘴。从辰时吵到酉时,天黑了,什么也没定下来。

更麻烦的是,镇上办事的人来问结果——桥修不修?粮分不分?——长老们你看我我看你,各说各的。办事的人不知道该听谁的,只好回去。

等了一年,南街的桥塌了。学堂的瓦也没补。

二、轮值的主席,过半的票

镇上有个年轻人叫阿序,在京城念过书。回乡看见议事厅这副光景,他找到七位长老。

"叔伯们,你们不是想法不对,是议事的法子不对。"

七位长老一齐看向他。

"我先问一句——你们认不认一个道理:一件事,只要超过一半的人同意,就算定了。"

赵长老说:"那当然。四个同意,就算。"

"好。"阿序从怀里掏出一个铜铃,"那我再问——你们愿不愿意,每次议事的时候,指定一个人来'主持'?这个人摇铃开会、收集提议、主持投票。"

"轮流当?"钱长老问。

"轮流当。"阿序说,"每人当一阵子,叫一'任'。一任之内,所有提议都由他发起。他提出方案,你们投票——过半就算通过。通不过?他改方案再投。"

孙长老皱起眉头:"那要是他提出了毛病的主意呢?"

"任期到了就换人。"阿序说,"而且每一项决议,都要记在议事簿上——第一条、第二条、第三条——按顺序编号,永不翻案。"

七位长老面面相觑。这法子简单,但以前没人提过。

第二天,他们试了。

赵长老摇铃,当第一任主席。他提"修南街的桥",投票:四票赞成,三票反对。过了。记在议事簿第一条。

第二任轮到钱长老。他提"补学堂的瓦",投票:五票赞成,两票反对。过了。记在议事簿第二条。

第三任孙长老。他提"开镇粮仓接济孤寡",投票:六票赞成,一票反对。过了。记在议事簿第三条。

一天定了三件事,比过去一年还多。

三、主席睡着了,新主席顶上

好景不长。有一天轮到郑长老当轮值主席。他年纪大了,在议事厅坐到一半,靠在椅背上睡着了——睡了大半个时辰。镇上来了三拨人请示,全被堵在门外。

阿序看到这一幕,又给规则加了一条。

"每一任主席,每隔一盏茶的功夫,必须摇一下铃。铃声告诉大家:主席还在、还在管事。这铃声叫'心跳'。"

"如果隔了三盏茶,铃还没响呢?"钱长老问。

"那就说明主席不在了——不管他是睡着了、出门了、还是病了。你们六个人立刻推举一位新主席,任期从'下一任'开始。旧主席醒来以后,发现自己不是主席了,就得按普通长老的身份继续议事。"

"那旧主席在职时定下的决议呢?"李长老追问。

"全部保留。"阿序说,"议事簿上的每一条决议,都经过了过半票数,永久有效。新主席上任,第一件事就是把议事簿从头到尾对一遍——自己的簿子和前任的簿子,一条不差。"

"怎么对?"

"前任的簿子上,每一条都有编号、都有票数。少一条?补上。编号对不上?按前任的来。就这么简单——新主席不另起炉灶,只在旧簿子上往下续。"

长老们沉默了一会儿。郑长老开口了:"就是说,主席可以换,但主席答应过的事不能赖。"

"对。"

"这规矩好。"

四、风雨夜,七个人只到了四个

那年秋天,一场大风雨把青山镇的桥冲断了——不光是南街的,北街的也断了。

七位长老连夜议事。这次轮到吴长老当主席。他先摇了铃,然后提第一条:"紧急征调全镇工匠,先搭浮桥。"

投票:在场的四个长老全数赞成。但还不够——七个长老的过半是四票,恰好够。过。记在议事簿第十七条。

第二条:"开镇粮仓,拨三百石粮食救济被困人家。"

投票:三个赞成,一个反对。不过半——风雨太大,另外三位长老没能赶来。

吴长老皱眉:"我们在场四个,三票赞成。能不能算过?"

阿序站在旁边,平静地说:"过半,是全部七个人的过半。三票不够,必须是四票。哪怕三位长老没到,规矩不变。你们没凑够四票,这条就不能记正册——最多记在'候议'栏里,等风停以后重新投。"

吴长老愣住了。片刻后,他点了点头:"宁可少记一条,也不能记一条假的。"

"对。议事簿上的每一条,都是过了全数过半真金白银的票。"

风雨停了。第二天,三位缺席的长老赶到了。吴长老把"拨粮三百石"重新提了一遍:这回七票全过。记入议事簿正册第十八条。

五、人心不是靠嘴拢齐的

又过了大半年。青山镇大小事务定了六十几条,件件有票可查、有号可追。十里八乡都来取经。

有个外乡的长老拉着阿序问:"你们这个轮值主席的法子,到底好在哪里?"

阿序想了想,伸出三根手指:

"第一,任何时候只有一个人主持,不会七嘴八舌。 主席在任期内,所有提议由他发起。别人有想法?告诉主席,主席代为提案。"

"第二,每一条决议都有编号、都有票数,什么时候查都清清楚楚。 谁也翻不了旧账,谁也编不了假票。"

"第三——"他顿了顿,"主席可以换,决议不会翻。哪怕风雨把半个议事厅冲走了,活下来的长老凑够四个,手里的旧簿子一拼,全部记录都在。新主席上任,先补簿子再议事。"

外乡长老沉默了很久。最后他说:"原来拢齐人心,不是靠谁的嗓门大。"

人心不是靠嘴拢齐的,是靠规矩。规矩到了,人心自己就拢了。

技术解读

Raft 是一种分布式共识算法,由 Diego Ongaro 和 John Ousterhout 于 2014 年在 USENIX ATC 发表论文《In Search of an Understandable Consensus Algorithm》中提出。它的设计目标很明确:让共识算法变得"可理解"。在此之前,Paxos(Lamport, 1989)一直是共识算法的标准,但因其难以理解和实现,工业界一直期待一种更直观的替代方案。Raft 通过将共识问题分解为三个相对独立的子问题——领导者选举日志复制安全性——成功做到了这一点。如今,etcd、TiKV、Consul 等众多分布式系统都基于 Raft 实现。

核心概念回顾

概念 通俗解释
共识算法 让多个节点对同一个值(或一系列值)达成一致的协议,即使部分节点故障也能正常工作
Leader(领导者) 集群中唯一负责接收客户端请求并将其复制到所有节点的角色,Raft 在任何时刻最多有一个有效的 Leader
Term(任期) 时间被划分为连续的任期,每个任期最多有一个 Leader。任期号单调递增,是 Raft 中的逻辑时钟
Heartbeat(心跳) Leader 周期性向所有 Follower 发送的消息,既用于维持权威,也用于阻止 Follower 发起新的选举
Leader Election(领导者选举) 当 Follower 在超时时间内未收到心跳,就转为 Candidate,增加任期号并发起投票。获得多数票的 Candidate 成为新 Leader
Log Replication(日志复制) Leader 将客户端请求追加到自己的日志中,然后并行复制给所有 Follower。当日志条目被多数节点确认后,Leader 将其"提交"并应用到状态机
Quorum(多数派 / 过半) 超过半数节点同意即为通过。N 个节点的集群可以容忍最多 (N-1)/2 个节点故障
Committed(已提交) 一旦日志条目被多数节点持久化,就被标记为"已提交",保证永远不会被未来 Leader 覆盖
Log Matching Property 如果两个日志在相同索引和任期号上有一条记录,则它们在该索引之前的所有记录完全一致

故事中的隐喻对照

故事元素 映射的技术概念 解释
七位长老 Raft 集群的 7 个节点 典型 Raft 集群使用奇数个节点(通常 3、5、7),以便形成多数派
议事厅 分布式系统中的集群 所有节点在一个逻辑集群中协同工作
轮值主席 Leader(领导者) 每个任期只有一个 Leader,负责发起所有提案并协调投票
任期 / 轮值 Term(任期号) 时间被划分为连续任期,每个任期最多一个 Leader。任期号单调递增
铜铃 / 铃声 Heartbeat(心跳) Leader 周期性发送心跳维持权威,Follower 通过心跳判断 Leader 是否存活
"三盏茶没摇铃就推举新主席" Election Timeout(选举超时) Follower 在随机超时时间内未收到心跳,就发起选举
"四个赞成算通过" Quorum(多数派) 7 个节点的过半 = 4。任何操作必须得到多数节点确认
议事簿 Raft Log(日志) 所有操作按顺序记录在日志中,每个条目有唯一索引
第一条、第二条、第三条编号 Log Index(日志索引) 日志中的条目按递增索引号排列,保证顺序
新主席先补簿子再议事 日志复制到新 Leader 新 Leader 上任后确保自己的日志与前任一致,包含所有已提交条目
"以前通过的决议永久有效" Committed entries are immutable 已提交的日志条目永远不会被覆盖,这是 Raft 安全性的核心保证
过半票数 + 编号不可翻案 Log Matching Property 两个日志如果在相同索引和任期号上一致,则该索引之前的所有条目都一致
风雨夜只到四个人 网络分区 / 节点故障 部分节点不可达时,集群仍能通过多数派继续工作
三票赞成不过半 多数派的要求 即使部分节点故障,操作仍需获得"总节点数"的多数,而非"在线节点数"的多数
"候议"栏 未提交的日志条目 未获多数确认的条目暂未提交,可能被后续 Leader 覆盖或重试
外乡长老的问题 共识算法的可理解性 Raft 的设计目标就是比 Paxos 更直观、更容易实现

为什么这个故事对应 Raft?

  1. "任何时候只有一个人主持"对应 Raft 的强 Leader 模型。 Raft 的所有日志流都是从 Leader 流向 Follower,不存在 Follower 直接发起提案的路径。这个设计极大简化了共识逻辑——你只需要保证 Leader 的日志是正确的。

  2. "主席摇铃告诉大家我还在"是心跳机制的核心作用。 心跳在 Raft 中有双重功能:一是阻止 Follower 发起不必要的选举(维持权威),二是携带日志复制消息(AppendEntries RPC)。

  3. "过半才算通过"是 Quorum 机制。 7 个节点的过半是 4。这意味着即使 3 个节点同时故障,集群仍能正常工作。任何两个多数派必然有交集——这保证了不会有两个不同的 Leader 同时提交冲突的日志。

  4. "风雨夜四个人通过了一条,三票通不过第二条"精确对应了网络分区下的多数派行为。 即使部分节点不可达,只要多数派仍在,Leader 就能继续提交日志。但操作必须获得"总节点数"的多数,而非"当前在线节点"的多数。

  5. "新主席上任先补簿子"是 Leader 选举后的日志一致性检查。 新 Leader 必须确保自己的日志包含所有已提交条目。Raft 通过比较日志的索引和任期号来实现这一点——新 Leader 的日志必须至少和前任一样"新"。

  6. "主席可以换,决议不会翻"是 Raft 安全性的最高原则。 一旦某条日志被提交(获得多数确认),任何未来的 Leader 都不能覆盖它。这是 Raft 与一些弱一致性协议的关键区别。

  7. "宁可少记一条,也不记一条假的"反映了强一致性系统对正确性的优先。 Raft 宁可暂时不可用(无法提交新日志),也不会提交一个未获多数确认的条目——这保证了不会出现"假共识"。

后记:分布式共识是计算机科学中最难的问题之一。Raft 的优雅之处在于——它没有试图发明全新的数学原理,而是把 Paxos 的核心思想分解成人脑更容易理解的模块:选主、复制、安全。下次你看到 etcd 里那些编号递增的日志条目时,不妨想想青山镇议事厅里那张泛黄的议事簿——每一条都有编号、都有票数、都永不翻案。共识不是让所有人都同意,而是让所有人都知道什么已经被同意。