深入 Elasticsearch(01):Node、Cluster 与集群状态
上一篇解决了 ES 的核心数据结构是什么——倒排索引。这一篇进入 ES 的运行时架构。
一个 Elasticsearch 集群不是若干台机器简单地连在一起。集群内部有明确的角色分工、中心化的状态管理和分布式的数据路径。理解这些,才能在后续文章中准确地定位每个机制发生在哪一层。
本文只抓一个问题:一个 ES 集群由哪些角色的节点组成,它们之间通过什么机制协调。
节点角色
一个 Elasticsearch 进程启动后就是一个节点(node)。多个节点通过相同的 cluster.name 组成一个集群(cluster)。每个节点可以承担一个或多个角色,角色通过配置文件或启动参数指定。
ES 8.x 中的主要节点角色:
| 角色 | 配置值 | 职责 |
|---|---|---|
| Master-eligible | master |
参与 master 选举,管理集群状态 |
| Data | data |
存储数据分片,执行搜索和聚合 |
| Data Content | data_content |
存储非时序数据 |
| Data Hot/Warm/Cold/Frozen | data_hot 等 |
分层存储(ILM 相关) |
| Ingest | ingest |
执行写入前的数据预处理管道 |
| Coordinating only | 不配置任何角色 | 接收客户端请求,路由到相关 shard,聚合结果返回 |
| ML | ml |
运行机器学习任务 |
| Remote Cluster Client | remote_cluster_client |
跨集群搜索的客户端节点 |
| Transform | transform |
运行 transform 任务 |
| Voting Only | voting_only |
参与选举投票但不会当选 master |
生产环境中通常将角色分离:专门的 master 节点(3 个,只负责集群状态管理)、专门的 data 节点(承载存储和搜索负载)、专门的 coordinating 节点(作为负载均衡器接收客户端请求)。
小规模集群或开发环境中,一个节点可以同时承担所有角色。默认配置下,一个节点同时是 master-eligible、data、ingest 和 coordinating 节点。
三种核心角色在集群中的分工和交互关系如下。这张图回答的问题是:客户端请求进入集群后,经过哪些节点、由谁执行什么操作。
flowchart TB
CLIENT["Client"] --> COORD["Coordinating Node<br/>请求路由 + 结果聚合"]
COORD --> DATA1["Data Node 1<br/>Shard 0 primary"]
COORD --> DATA2["Data Node 2<br/>Shard 1 primary"]
COORD --> DATA3["Data Node 3<br/>Shard 0 replica"]
MASTER["Master Node<br/>集群状态管理"] -.->|广播 cluster state| COORD
MASTER -.->|广播 cluster state| DATA1
MASTER -.->|广播 cluster state| DATA2
MASTER -.->|广播 cluster state| DATA3
style MASTER fill:#fce4ec,stroke:#d32f2f
style COORD fill:#e8f4fd,stroke:#1a73e8
style DATA1 fill:#e6f4ea,stroke:#34a853
style DATA2 fill:#e6f4ea,stroke:#34a853
style DATA3 fill:#e6f4ea,stroke:#34a853
实线表示数据路径(搜索和写入),虚线表示控制路径(集群状态分发)。数据路径不经过 Master,这是 ES 能够水平扩展搜索吞吐的前提。
集群状态
集群状态(cluster state)是 Elasticsearch 集群的核心元数据结构。它描述了整个集群"当前应该长什么样"。
cluster state 的三个主要组成部分:
1 | |
cluster state 的关键特性:
- 只有 master 节点可以修改 cluster state。所有修改请求(创建 index、修改 mapping、shard 分配变更)都必须经过 master。
- master 修改完成后,将新版本的 cluster state 全量分发给集群中的每个节点。
- 每个节点在本地维护一份 cluster state 的副本。当节点需要知道某个 shard 在哪个节点上时,直接查本地副本,不需要再问 master。
- cluster state 的更新是全量的,不是增量的。每次变更都分发完整的新状态。这在集群规模很大(数千个 index、数万个 shard)时可能成为瓶颈。
数据路径 vs 控制路径
理解 ES 架构的关键是区分两条路径:
1 | |
数据路径是去中心化的。一个搜索请求不需要经过 master 节点。coordinating node 根据本地的 cluster state 副本知道目标 shard 在哪些节点上,直接把请求发过去。
控制路径是中心化的。任何集群元数据的变更都必须经过 master。这保证了集群状态的一致性——不会出现两个节点对"shard 0 应该在哪里"有不同意见。
把两条路径画在一张序列图中,能直接对比它们的参与角色和通信步骤。
sequenceDiagram
participant C as Client
participant CO as Coordinating Node
participant M as Master Node
participant D1 as Data Node 1
participant D2 as Data Node 2
rect rgb(232, 244, 253)
Note over C,D2: 数据路径(搜索)
C->>CO: 搜索请求
CO->>D1: scatter 到 shard 0
CO->>D2: scatter 到 shard 1
D1-->>CO: 局部结果
D2-->>CO: 局部结果
CO-->>C: 合并后返回
end
rect rgb(252, 228, 236)
Note over C,D2: 控制路径(创建 index)
C->>M: 创建 index
M->>M: 修改 cluster state
M->>CO: 广播新状态
M->>D1: 广播新状态
M->>D2: 广播新状态
end
上半部分的数据路径中,Master 完全不参与;下半部分的控制路径中,所有变更必须经过 Master 再全量广播。两条路径的分离使得日常搜索吞吐不受元数据变更的影响。
实验:观察节点和集群状态
查看集群中所有节点及其角色:
1 | |
返回示例(单节点集群):
1 | |
node.role 列中的字母代表不同角色:c=cold, d=data, f=frozen, h=hot, i=ingest, l=ml, m=master, r=remote_cluster_client, s=content, t=transform, w=warm。* 表示当前节点是 active master。
查看 cluster state 的核心结构:
1 | |
返回示例(只显示第一个 shard 的路由信息):
1 | |
routing table 明确记录了每个 shard 当前在哪个 node 上、是 primary 还是 replica、状态是 STARTED 还是 RELOCATING 等。
查看集群健康状态:
1 | |
1 | |
status 的含义:green=所有 primary 和 replica 都已分配,yellow=所有 primary 已分配但有 replica 未分配,red=有 primary 未分配。单节点集群中 replica 无法分配到其他节点,所以如果 index 配置了 replica,状态通常是 yellow。
模式提炼:中心化元数据 + 去中心化数据路径
1 | |
| 维度 | 控制路径 | 数据路径 |
|---|---|---|
| 拓扑 | 中心化(master 单点写) | 去中心化(任何节点可接收请求) |
| 一致性 | 强一致(master 顺序更新) | 最终一致(replica 异步可见) |
| 瓶颈风险 | cluster state 过大导致分发慢 | shard 数过多导致 scatter-gather 开销 |
| 故障影响 | master 故障触发重新选举 | 单个 data node 故障触发 shard 重分配 |
这种"中心化元数据 + 去中心化数据路径"的模式在分布式系统中非常常见。
工程迁移表
| 概念 | Elasticsearch | Apache Kafka | ZooKeeper | etcd |
|---|---|---|---|---|
| 元数据存储 | Cluster state(master 管理) | Controller metadata(KRaft / ZK) | ZNode tree | key-value store |
| 元数据写入 | 只有 master 可写 | 只有 controller 可写 | leader 写,follower 转发 | leader 写,follower 转发 |
| 元数据分发 | master 全量广播 | controller 增量分发 | watch 通知 | watch 通知 |
| 数据路径 | coordinating → shard(去中心化) | producer → partition leader(去中心化) | N/A(不存储业务数据) | N/A |
| 选举机制 | 自 7.x 起基于类 Raft | KRaft(自 3.x) / ZooKeeper | ZAB 协议 | Raft |
常见误解
误解一:每个请求都要经过 master。 搜索和写入操作走数据路径,不经过 master。master 只负责集群状态的变更(创建 index、shard 分配等)。如果每个搜索都要经过 master,master 早就成为瓶颈了。
误解二:coordinating node 是一个特殊角色。 实际上每个 ES 节点都天然具备 coordinating 能力。所谓"coordinating only"节点只是一个不承担其他角色的节点,专门用来接收请求和聚合结果。
误解三:cluster state 是增量同步的。 截至当前版本,cluster state 的更新是全量分发的。这意味着集群中有几千个 index 时,每次创建新 index 都会导致一次全量 cluster state 广播。这是 ES 在超大规模集群中的一个已知挑战。
练习
-
启动一个单节点 ES 集群,用
GET _cat/nodes?v查看节点角色。然后在配置文件中修改node.roles,只保留data和master,重启后再次查看,观察角色变化。 -
创建一个带 1 replica 的 index,用
GET _cluster/health查看集群状态(预期是 yellow)。然后用PUT /your-index/_settings {"index": {"number_of_replicas": 0}}将 replica 设为 0,再次查看(预期变 green)。 -
用
GET _cluster/state/routing_table查看完整的 routing table,找到某个 index 的 shard 分布。理解 routing table 中primary、state、node字段的含义。
系列导航
| 上一篇 | 下一篇 |
|---|---|
| 导读:为什么 Elasticsearch 的核心是一张倒排索引 | Lucene 内部:Segment、倒排索引与 Doc Values |
