很多人学习 Raft 协议时,都会产生一个疑问:Elasticsearch 的 Master 选举是不是 Raft?
答案很明确:
现在的机制非常像 Raft,但早期完全不是。而且,早期选主机制还引发过不少线上事故。
今天,我们就从真实的工程问题出发,深入剖析 Elasticsearch 选主机制的演进之路,讲清楚它是如何一步步变得健壮的。
一、分布式系统的噩梦:什么是“脑裂”?
在分布式系统里,最可怕的故障往往不是节点宕机,而是:
同时出现两个 Master
这种情况被称为 Split Brain(脑裂)。
一旦发生脑裂,集群会陷入混乱:
- 两个 Master 可能同时修改集群状态。
- 数据分片可能被冲突地分配到不同节点。
- 集群的元数据(Metadata)会出现不一致。
- 故障恢复时,极有可能导致数据丢失。
在 Elasticsearch 6.x 及更早的版本中,脑裂问题并不少见。
二、早期的设计:Zen Discovery 与手动配置
在 7.0 版本之前,Elasticsearch 使用一套名为 Zen Discovery 的模块来处理节点发现与 Master 选举。
它的核心思想听起来很合理:多数派(Quorum)选举。即,一个节点必须得到超过半数的 Master-eligible 节点同意,才能成为 Leader。
但这个机制依赖于一个至关重要的手动配置项:
discovery.zen.minimum_master_nodes
这个参数具体怎么用?我们来模拟一个场景。
假设你的集群有 3 个具备 Master 资格的节点。此时,minimum_master_nodes 的正确配置应该是:
minimum_master_nodes = 2
它的计算公式是 (N / 2) + 1,其中 N 是 Master-eligible 节点的总数。
如果这个参数配错了,比如设置成了 1,危险就来了。当发生网络分区(Network Partition)时:
A | B C
节点 A 在一侧,节点 B 和 C 在另一侧。由于 minimum_master_nodes=1,A 可以自举为 Master,B 和 C 这一侧也能成功选出一个 Master。
于是,集群中出现了两个“合法”的 Master:
两个 Master 同时存在
脑裂就此发生。
三、为何说这个设计是“危险”的?
关键在于,Zen Discovery 将集群安全的基石,完全寄托于“人工配置的正确性”。而在复杂的工程实践中,手动参数恰恰是最容易出错的环节:
- 集群扩容时,忘记更新此参数。
- 集群缩容时,同样忘记修改。
- 测试环境的配置错误地同步到了生产环境。
- 运维人员对计算公式理解有误。
历史上大量的 Elasticsearch 线上稳定性事故,都与 discovery.zen.minimum_master_nodes 配置不当有关。这促使 Elastic 官方在 7.x 版本中下决心重构整个协调子系统。
四、Elasticsearch 7.x 的重构:走向“类 Raft”
从 7.0 版本开始,Elasticsearch 彻底重写了负责选主和元数据一致性的子系统,并将其命名为 Cluster Coordination Subsystem。
官方文档明确指出,其设计 灵感来源于 Raft 协议(Inspired by Raft)。
请注意这里的措辞:是“借鉴灵感”而非“完全实现”。它吸收了 Raft 中关于安全性的核心思想,并针对 Elasticsearch 自身的数据模型和运维场景做了大量工程化裁剪。
五、新版选主机制的核心思想
让我们拆解一下这套新机制的几个关键点,它与 Raft 协议的相似之处一目了然。
1️⃣ 引入任期(Term)概念
和 Raft 完全一致,每一次新的选举周期都会产生一个递增的 Term 编号。
term + 1
一个旧任期的节点,无论因为什么原因(如网络延迟、进程暂停)重新加入集群,都无法再成为合法的 Master。这有效防止了“旧主复活,覆盖新状态”的问题。
2️⃣ 严格的多数派投票制
一个节点想要成为 Master,必须获得当前投票配置(Voting Configuration)中超过半数的节点投票。这是保证同一任期内最多只有一个 Leader 的根本原则。
3️⃣ 移除 minimum_master_nodes 手动配置
这是最具工程意义的一步。在 7.x 之后,你不再需要手动计算和配置这个参数。系统会根据当前的 Voting Configuration 自动判定多数派。
这从根本上消除了因人工配错而导致脑裂的可能性。
4️⃣ 引入投票配置(Voting Configuration)
这是一个工程上的优化设计。每当集群拓扑发生变更时——例如增加或移除 Master 节点——都会生成一个新的 Voting Configuration。
Voting Configuration
它明确记录了在当前任期内,哪些节点拥有投票权。这类似于 Raft 协议中的集群成员变更(Membership Change)机制,但实现上更贴合 Elasticsearch 自身的管理 API 和运维习惯。
5️⃣ 发布-确认的提交机制
新选出的 Master(称为 Elected Master)并不会立即行使权力。它必须完成一个关键步骤:
向多数节点发布新的 Cluster State
并等待这些节点的确认(Ack)。只有在收到足够多的确认,即该状态被“提交”后,它才正式成为活跃的 Master(Active Master)。
这个机制与 Raft 中 Leader 将日志复制到多数节点后才提交(Commit)的模型非常相似。
六、一次完整的选举流程(工程视角)
当活跃 Master 失效后,集群的选举流程可以概括为以下几步:
Step 1:节点成为候选人
检测到 Master 失联的节点会自增 Term,并进入 Candidate 状态,向其他投票节点发起投票请求。
Step 2:节点投票逻辑
一个节点在收到投票请求后,会基于以下条件决定是否投票:
- 自己在本任期尚未投过票。
- 请求方的 Term 不低于自己已知的 Term。
- 请求方所知的集群状态不落后于自己。
Step 3:赢得选举
某个候选人如果获得了超过半数的投票,即成为“当选 Master”(Elected Master)。
Step 4:发布并确认集群状态
当选 Master 将包含新任期和自己身份的集群状态发布出去,并等待多数节点的确认。
Step 5:正式履职
在获得足够确认后,当选 Master 转变为活跃 Master,开始接收和处理各类集群操作请求,如索引创建、分片分配、Mapping 更新等。
七、与标准 Raft 协议的关键差异
尽管高度相似,但 Elasticsearch 的协调机制并非标准的 Raft 实现,主要区别在于:
① 复制对象不同
- Raft:复制的是顺序化的操作日志,通过日志来驱动所有状态机(如 MySQL、Redis 等)达成一致。
- Elasticsearch:主要对 Cluster State(集群状态,即元数据) 做一致性控制。而真正的数据分片(Shard)本身,是通过主副分片间的同步(Replication)机制来保证的。
② 协议范围不同
- Raft:是一个通用的状态机复制协议。
- Elasticsearch:是一个专注于元数据一致性的协议,范围更聚焦。
③ 工程优化侧重点不同
Elasticsearch 的设计优先考虑了自身场景的工程稳定性:
- 快速启动与恢复:优化选举速度和节点加入流程。
- 耐受网络抖动:在网络不稳定时能更快恢复服务。
- 绝对避免脑裂:这是设计的核心红线。
- 自动化与安全性:减少人工干预,防止配置错误。
八、为什么说现在“非常像 Raft”?
因为它完整具备了 Raft 协议中保证安全性的几个核心要素:
- 任期(Term)
- 领导者选举必须获得多数派投票
- 状态提交需经多数派确认
- 安全的集群成员变更管理
但它也缺失了 Raft 作为通用协议的一些组成部分:
- 通用的、顺序化的操作日志复制模型。
- 严格的 Leader 追加日志(Append-Entries)的同步方式。
- 完整的状态机抽象。
因此,更准确的定位是:一个深受 Raft 启发、为解决元数据一致性而深度工程化的“类 Raft”集群协调协议。
九、现在的 Elasticsearch 还会脑裂吗?
在 7.x 及之后的版本中,在正常的网络模型和运维操作下,几乎不可能发生脑裂。
原因就在于上述机制的保证:
- 选主必须获得自动计算的多数派同意。
- 严格的任期校验阻止旧主干扰。
- 任何集群状态变更都需要多数派确认后才能生效。
这套机制在实践中已经非常成熟和稳定。
十、总结与启示
Elasticsearch 集群协调机制的演进历程,给了我们一个深刻的工程启示:
经典的理论协议不等于最终的工程实现。一个成熟的工程系统,必须根据自身的核心诉求进行合理的场景裁剪。
它没有盲目照搬 Raft,而是:
- 精准识别核心问题:对于 Elasticsearch,核心是保证集群元数据(Cluster State)的一致性,而非所有数据。
- 借鉴安全性原则:牢牢抓住 Raft 中关于选举安全、提交安全的核心思想。
- 进行深度工程优化:移除易错的手动配置,设计更贴合自身运维模式的成员管理,优化恢复速度。
这种“取其神,塑其形”的设计方式,正是构建稳定、可靠分布式系统的成熟思路。对于开发者而言,理解这种从理论到实践的落地过程,比单纯学习协议本身更有价值。如果你想了解更多分布式系统设计背后的权衡与细节,欢迎来 云栈社区 与大家一起交流探讨。