上一篇解决了 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
2
3
4
5
6
7
8
9
Cluster State
├── Metadata
│ ├── Index metadata(index settings, mappings, aliases)
│ ├── Templates(index templates, component templates)
│ └── Persistent cluster settings
├── Routing Table
│ └── Index → Shard → Node 的映射关系
└── Nodes
└── 集群中所有节点的信息(角色、地址、属性)

cluster state 的关键特性:

  • 只有 master 节点可以修改 cluster state。所有修改请求(创建 index、修改 mapping、shard 分配变更)都必须经过 master。
  • master 修改完成后,将新版本的 cluster state 全量分发给集群中的每个节点。
  • 每个节点在本地维护一份 cluster state 的副本。当节点需要知道某个 shard 在哪个节点上时,直接查本地副本,不需要再问 master。
  • cluster state 的更新是全量的,不是增量的。每次变更都分发完整的新状态。这在集群规模很大(数千个 index、数万个 shard)时可能成为瓶颈。

数据路径 vs 控制路径

理解 ES 架构的关键是区分两条路径:

1
2
3
4
5
6
控制路径(经过 master):
创建 index → master 修改 cluster state → 广播新状态 → 各节点 apply

数据路径(不经过 master):
写入文档 → coordinating node → 路由到目标 shard 的 primary → 转发到 replica
搜索 → coordinating node → 广播到所有相关 shard → 收集结果 → 返回客户端

数据路径是去中心化的。一个搜索请求不需要经过 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
GET _cat/nodes?v&h=name,node.role,master,heap.percent,ram.percent,cpu

返回示例(单节点集群):

1
2
name           node.role  master heap.percent ram.percent cpu
es-node-1 cdfhilmrstw * 45 72 12

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
GET _cluster/state?filter_path=metadata.cluster_uuid,routing_table.indices.*.shards.0

返回示例(只显示第一个 shard 的路由信息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"metadata": {
"cluster_uuid": "abc123..."
},
"routing_table": {
"indices": {
"test-index": {
"shards": {
"0": [
{
"state": "STARTED",
"primary": true,
"node": "node-id-1",
"shard": 0,
"index": "test-index"
}
]
}
}
}
}
}

routing table 明确记录了每个 shard 当前在哪个 node 上、是 primary 还是 replica、状态是 STARTED 还是 RELOCATING 等。

查看集群健康状态:

1
GET _cluster/health
1
2
3
4
5
6
7
8
9
{
"cluster_name": "my-cluster",
"status": "green",
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 5,
"active_shards": 5,
"unassigned_shards": 0
}

status 的含义:green=所有 primary 和 replica 都已分配,yellow=所有 primary 已分配但有 replica 未分配,red=有 primary 未分配。单节点集群中 replica 无法分配到其他节点,所以如果 index 配置了 replica,状态通常是 yellow。

模式提炼:中心化元数据 + 去中心化数据路径

1
2
3
4
5
6
7
8
9
10
11
12
cluster state(中心化)         data path(去中心化)
┌──────────────┐ ┌──────────────────────────┐
│ master │ │ client │
│ (single) │──广播──→ │ ↓ │
│ │ 全量状态 │ coordinating node │
└──────────────┘ │ ↓ (scatter) │
│ shard1 shard2 shard3 │
│ ↓ (gather) │
│ coordinating node │
│ ↓ │
│ client │
└──────────────────────────┘
维度 控制路径 数据路径
拓扑 中心化(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 在超大规模集群中的一个已知挑战。

练习

  1. 启动一个单节点 ES 集群,用 GET _cat/nodes?v 查看节点角色。然后在配置文件中修改 node.roles,只保留 datamaster,重启后再次查看,观察角色变化。

  2. 创建一个带 1 replica 的 index,用 GET _cluster/health 查看集群状态(预期是 yellow)。然后用 PUT /your-index/_settings {"index": {"number_of_replicas": 0}} 将 replica 设为 0,再次查看(预期变 green)。

  3. GET _cluster/state/routing_table 查看完整的 routing table,找到某个 index 的 shard 分布。理解 routing table 中 primarystatenode 字段的含义。

系列导航

上一篇 下一篇
导读:为什么 Elasticsearch 的核心是一张倒排索引 Lucene 内部:Segment、倒排索引与 Doc Values

参考资料