"两个 Master 同时发号施令 ------ 比宕机可怕一万倍,因为系统'活着但疯了'。"
脑裂(Split Brain)------ 数据永久错乱
不是"查不到",是"查出鬼":瑟瑟发抖
场景 1 :同一个 doc_id=1,两次查询返回不同内容:这放到sql上叫什么?大声说出来!
// 第一次
{ "_id": "1", "_version": 3, "name": "Alice" }
// 第二次(刷新后)
{ "_id": "1", "_version": 2, "name": "Bob" } // ← 版本号倒退!
场景 2:索引元数据冲突
- 节点 A 认为索引有 5 个分片
- 节点 B 认为索引有 3 个分片
GET _cluster/state返回混乱结构
场景 3:写入成功但数据丢失
- 客户端收到
201 Created - 但稍后查询发现文档不存在
根因:Master 选举失控(6.x 及以下)
分布式共识基础
- ES 集群必须有且仅有一个 Master 节点(负责元数据变更:创建索引、分配分片等)
- 选举规则(6.x):多数派(quorum)同意即可当选
脑裂触发条件
- 集群有 3 个 master-eligible 节点:
M1, M2, M3 - 网络分区:
M1与M2,M3断连 - 未设置
discovery.zen.minimum_master_nodesM1认为自己是唯一节点 → 自选为 MasterM2,M3形成多数派 → 选M2为 Master
为什么数据会错乱?
M1接受写入 → 更新分片 AM2接受写入 → 更新分片 B- 网络恢复后,ES 不会合并数据,而是随机保留一个状态
- 丢失的数据永远找不回来
🔥 关键点 :脑裂期间的写入 = 数据黑洞。
🛠️ 解决方案:两代防御体系
方案 1:升级到 7.x+(推荐!)其实8+更好
- 7.0+ 移除了
minimum_master_nodes - 引入 基于 Raft 的新协调层(Coordination Module)
- 自动防脑裂 :要求 法定人数(quorum)写入日志成功才提交
- 即使网络分区,最多只有一个 Master 能接受写入
方案 2:6.x 必须手动配置(如无法升级)
# elasticsearch.yml(所有 master-eligible 节点必须一致!)
node.master: true
node.data: false
# ⚠️ 关键配置:法定人数 = N/2 + 1
discovery.zen.minimum_master_nodes: 2 # 3 节点集群
# discovery.zen.minimum_master_nodes: 3 # 5 节点集群
❗ 错误配置示例:
- 3 节点设
minimum_master_nodes: 1→ 等于没设! - 节点配置不一致 → 选举失败
🔍 如何检测是否已脑裂?
方法 1:检查 Master 节点数量
# 正常:只返回 1 个
GET _cat/nodes?h=name,master&v | grep "*"
# 脑裂:返回多个带 "*" 的节点
方法 2:检查集群状态
GET _cluster/health
{
"status": "red",
"number_of_nodes": 5,
"number_of_data_nodes": 3,
"active_primary_shards": 10,
"relocating_shards": 0,
"initializing_shards": 5, // ← 异常初始化
"unassigned_shards": 10 // ← 大量未分配
}
方法 3:监控日志关键词
master_leftzen-disco-elected-as-master(短时间内多次出现)failed to send join request to master
脑裂后如何恢复?(痛苦但必要)
⚠️ 警告:无完美方案!数据可能永久丢失。
步骤 1:立即停止所有写入
- 防止进一步污染数据
步骤 2:确定"权威"子集群
- 选择包含 更多数据节点 或 最新数据 的分区
- 通过
GET /_all/_search?size=1&sort=@timestamp:desc对比
步骤 3:强制重启非权威集群
- 停掉"错误"的 Master 节点
- 清空其数据目录(
path.data) - 重新加入集群(作为新节点)
步骤 4:重建索引(最安全)
- 从备份恢复
- 或从源系统重放数据
现实 :脑裂后的集群不应信任,建议重建。
防脑裂配置模板6.x
# ========================
# 集群基础信息
# ========================
cluster.name: prod-es-cluster # 所有节点必须一致
# ========================
# 节点角色(Master 专用)
# ========================
node.name: es-master-01 # 每个节点唯一
node.master: true # 可参与 Master 选举
node.data: false # 不存数据(专用 Master)
node.ingest: false # 不做预处理
# ========================
# 网络绑定
# ========================
network.host: 0.0.0.0 # 绑定所有接口(生产建议指定内网IP)
http.port: 9200
transport.port: 9300 # 节点间通信端口
# ========================
# 脑裂防护核心配置(Zen Discovery)
# ========================
# 初始 Master 候选列表(所有 Master 节点 IP 或主机名)
discovery.zen.ping.unicast.hosts:
- "10.0.1.10:9300" # es-master-01
- "10.0.1.11:9300" # es-master-02
- "10.0.1.12:9300" # es-master-03
# 法定人数 = N/2 + 1 → 3 节点必须设为 2
# 作用:选举时至少 2 个节点同意才能选出 Master
discovery.zen.minimum_master_nodes: 2
# 超时设置(避免因短暂网络抖动误判)
discovery.zen.ping_timeout: 30s
discovery.zen.fd.ping_timeout: 30s
# ========================
# 安全加固(可选但推荐)
# ========================
# 防止非集群节点加入
discovery.zen.no_master_block: write # 无 Master 时只阻塞写入(默认)
# ========================
# 内存与线程(Master 节点轻量)
# ========================
indices.memory.index_buffer_size: "10%"
thread_pool:
management.size: 2 # Master 节点管理线程少即可
# ========================
# 日志级别(便于排查选举问题)
# ========================
logger.discovery: DEBUG # 关键!记录选举过程
| 配置项 | 为什么重要 |
|---|---|
discovery.zen.minimum_master_zones: 2 |
防脑裂核心:3 节点下,单节点无法自选为 Master |
unicast.hosts 列出所有 Master |
避免通过广播发现非预期节点 |
no_master_block: write |
无 Master 时允许读,提升可用性 |
logger.discovery: DEBUG |
出现选举异常时,日志会记录详细过程 |
选举对照
| 特性 | Elasticsearch 6.x(Zen Discovery) | Elasticsearch 7.x+(Coordination Layer) |
|---|---|---|
| 共识算法 | 自定义多数派投票 | 基于 Raft 的改进版 |
| 脑裂防护 | 依赖手动配置 minimum_master_nodes |
自动内置,无需配置 |
| 法定人数计算 | 用户指定 N/2+1 |
自动:(master_eligible_nodes / 2) + 1 |
| 配置项 | discovery.zen.minimum_master_nodes |
已移除(设置会报错) |
| 选举日志 | 无持久化日志 | 持久化到 data/<cluster_uuid>/nodes/0/_state/ |
| 网络分区行为 | 可能多主(若配置错误) | 最多一个 Master 可接受写入 |
| 升级兼容性 | --- | 从 6.x 升级需先正确配置 minimum_master_nodes |
| 官方态度 | 已废弃 | 唯一支持的协调机制 |
🔍 关键差异详解
1. Raft 的"持久化日志"机制
- 每次 Master 变更都写入 本地磁盘日志
- 新节点加入时,必须同步完整日志才能参与选举
- 杜绝了"失联节点回归后自立为王"
2. 自动法定人数
- 7.x+ 启动时自动计算:quorum = (number_of_master_eligible_nodes // 2) + 1
- 用户无法覆盖,从根本上消除配置错误
3. 写入安全性
- 7.x+ 要求:元数据变更必须被 quorum 节点持久化成功
- 6.x:仅需 quorum 节点"收到请求",不保证落盘
| 版本 | 行动 |
|---|---|
| ES 7.x+ | ✅ 默认安全,无需额外配置 ✅ 确保至少 3 个 master-eligible 节点 |
| ES 6.x | 🔒 必须设置 minimum_master_nodes = N/2 + 1 🔒 所有 Master 节点配置完全一致 🚨 尽快升级到 7.x+ 然后你会发现7+ 让你升级8+ |
朋友们,这升级是真的有用