忘机山人
#
https://appstore.lazycat.cloud/#/shop/detail/xu.deploy.elasticsearch
> 本章导读:本章将深入介绍 Elasticsearch 的架构设计,包括集群架构、节点类型、数据存储原理等,帮助读者理解 ES 的内部工作机制。
## 目录
- [前置知识](#前置知识)
- [集群架构概览](#集群架构概览)
- [节点类型详解](#节点类型详解)
- [数据存储原理](#数据存储原理)
- [分布式架构](#分布式架构)
- [本章小结](#本章小结)
- [参考资料](#参考资料)
## 集群架构概览
### 整体架构图
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 客户端请求 │
│ (REST API / Transport Protocol) │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Elasticsearch 集群 │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 协调层 (Coordinating) │ │
│ │ 接收请求 → 路由到分片 → 聚合结果 → 返回响应 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────┼──────────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Master │ │ Data │ │ Ingest │ │
│ │ Node │ │ Node │ │ Node │ │
│ │ │ │ │ │ │ │
│ │ • 集群状态 │ │ • 数据存储 │ │ • 数据预处理│ │
│ │ • 索引管理 │ │ • 搜索执行 │ │ • Pipeline │ │
│ │ • 分片分配 │ │ • 聚合计算 │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 存储层 (Lucene) │ │
│ │ 倒排索引 / Doc Values / 段文件 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### 架构组件说明
| 组件 | 职责 |
|------|------|
| 协调层 | 接收客户端请求,路由到正确的分片,聚合各分片结果 |
| Master 节点 | 管理集群元数据,处理索引创建/删除,分片分配决策 |
| Data 节点 | 存储数据,执行数据相关操作(CRUD、搜索、聚合) |
| Ingest 节点 | 执行 Ingest Pipeline,在索引前预处理文档 |
| 存储层 | 基于 Lucene 的底层存储,管理倒排索引和段文件 |
## 节点类型详解
### Master 节点
Master 节点负责集群级别的管理操作:
**职责**:
- 维护集群状态(Cluster State)
- 创建和删除索引
- 分片分配和重平衡决策
- 节点加入和离开处理
**配置**:
```yaml
# elasticsearch.yml
node.roles: [master]
```
**最佳实践**:
- 生产环境至少 3 个 Master-eligible 节点
- 专用 Master 节点不存储数据,资源需求较低
- 配置 `cluster.initial_master_nodes` 防止脑裂
```yaml
# 专用 Master 节点配置
node.roles: [master]
cluster.initial_master_nodes: ["master-1", "master-2", "master-3"]
```
### Data 节点
Data 节点负责数据存储和处理:
**职责**:
- 存储分片数据
- 执行 CRUD 操作
- 执行搜索和聚合
**配置**:
```yaml
# elasticsearch.yml
node.roles: [data]
```
**数据节点分层**(8.x 新特性):
| 角色 | 说明 | 适用数据 |
|------|------|----------|
| `data_hot` | 热数据节点 | 最新、频繁访问的数据 |
| `data_warm` | 温数据节点 | 较少访问的历史数据 |
| `data_cold` | 冷数据节点 | 很少访问的归档数据 |
| `data_frozen` | 冻结数据节点 | 极少访问,可搜索快照 |
| `data_content` | 内容数据节点 | 非时序数据 |
```yaml
# 热数据节点配置
node.roles: [data_hot]
# 温数据节点配置
node.roles: [data_warm]
```
### Coordinating 节点
协调节点(也称为客户端节点)专门处理请求路由:
**职责**:
- 接收客户端请求
- 将请求路由到相关分片
- 聚合各分片返回的结果
- 返回最终结果给客户端
**配置**:
```yaml
# 专用协调节点(不设置任何角色)
node.roles: []
```
**工作流程**:
```
客户端请求
│
▼
┌─────────────────┐
│ Coordinating │
│ Node │
└────────┬────────┘
│ 路由请求
┌────┴────┬────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│Shard 0│ │Shard 1│ │Shard 2│
└───┬───┘ └───┬───┘ └───┬───┘
│ │ │
└────┬────┴────────┘
│ 聚合结果
▼
┌─────────────────┐
│ Coordinating │
│ Node │
└────────┬────────┘
│
▼
客户端响应
```
### Ingest 节点
Ingest 节点在文档索引前执行预处理:
**职责**:
- 执行 Ingest Pipeline
- 数据转换和丰富
- 字段提取和格式化
**配置**:
```yaml
# elasticsearch.yml
node.roles: [ingest]
```
**Pipeline 示例**:
```json
PUT _ingest/pipeline/my-pipeline
{
"description": "处理日志数据",
"processors": [
{
"grok": {
"field": "message",
"patterns": ["%{IP:client_ip} - %{DATA:user} \\[%{HTTPDATE:timestamp}\\]"]
}
},
{
"date": {
"field": "timestamp",
"formats": ["dd/MMM/yyyy:HH:mm:ss Z"]
}
},
{
"geoip": {
"field": "client_ip"
}
}
]
}
```
### ML 节点
机器学习节点执行 ML 任务:
**职责**:
- 异常检测作业
- 预测分析
- 数据帧分析
**配置**:
```yaml
# elasticsearch.yml
node.roles: [ml]
xpack.ml.enabled: true
```
### 节点角色组合建议
**小型集群(开发/测试)**:
```yaml
# 所有节点都是全角色
node.roles: [master, data, ingest]
```
**中型集群**:
```
┌─────────────────────────────────────────────────────────┐
│ 3 x Master/Data 节点 │
│ node.roles: [master, data, ingest] │
└─────────────────────────────────────────────────────────┘
```
**大型集群**:
```
┌─────────────────────────────────────────────────────────┐
│ 3 x 专用 Master 节点 │
│ node.roles: [master] │
├─────────────────────────────────────────────────────────┤
│ N x 专用 Data 节点 │
│ node.roles: [data_hot] / [data_warm] / [data_cold] │
├─────────────────────────────────────────────────────────┤
│ 2+ x 协调节点 │
│ node.roles: [] │
├─────────────────────────────────────────────────────────┤
│ 2+ x Ingest 节点 │
│ node.roles: [ingest] │
└─────────────────────────────────────────────────────────┘
```
## 数据存储原理
### Lucene 基础
Elasticsearch 底层使用 Apache Lucene 作为搜索引擎库。每个分片本质上是一个 Lucene 索引。
```
┌─────────────────────────────────────────────────────────┐
│ Elasticsearch 分片 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Lucene Index │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │Segment 0│ │Segment 1│ │Segment 2│ ... │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### 倒排索引(Inverted Index)
倒排索引是全文搜索的核心数据结构:
**正向索引 vs 倒排索引**:
```
正向索引(文档 → 词项):
┌─────────┬────────────────────────────┐
│ Doc ID │ Content │
├─────────┼────────────────────────────┤
│ 1 │ "Elasticsearch is fast" │
│ 2 │ "Elasticsearch is scalable"│
│ 3 │ "Lucene is fast" │
└─────────┴────────────────────────────┘
倒排索引(词项 → 文档):
┌───────────────┬─────────────────────────┐
│ Term │ Posting List │
├───────────────┼─────────────────────────┤
│ elasticsearch │ [1, 2] │
│ fast │ [1, 3] │
│ is │ [1, 2, 3] │
│ lucene │ [3] │
│ scalable │ [2] │
└───────────────┴─────────────────────────┘
```
**倒排索引结构**:
```
┌─────────────────────────────────────────────────────────┐
│ 倒排索引 │
├─────────────────────────────────────────────────────────┤
│ Term Dictionary (词项字典) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ elasticsearch → offset: 0 │ │
│ │ fast → offset: 100 │ │
│ │ scalable → offset: 200 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Posting List (倒排列表) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Doc IDs: [1, 2] │ │
│ │ Term Frequency: [1, 1] │ │
│ │ Positions: [[0], [0]] │ │
│ │ Offsets: [[0-13], [0-13]] │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### Doc Values
Doc Values 是列式存储,用于排序、聚合和脚本访问:
```
倒排索引(搜索用): Doc Values(聚合/排序用):
Term → Doc IDs Doc ID → Field Value
"apple" → [1, 3] 1 → "apple"
"banana" → [2] 2 → "banana"
"cherry" → [4] 3 → "apple"
4 → "cherry"
```
**Doc Values vs Fielddata**:
| 特性 | Doc Values | Fielddata |
|------|------------|-----------|
| 存储位置 | 磁盘 | 内存 |
| 构建时机 | 索引时 | 查询时 |
| 适用类型 | keyword, numeric, date, ip, geo | text |
| 内存占用 | 低 | 高 |
| 默认状态 | 启用 | 禁用 |
```json
// 禁用 Doc Values(节省磁盘,但无法排序/聚合)
PUT /my-index
{
"mappings": {
"properties": {
"description": {
"type": "keyword",
"doc_values": false
}
}
}
}
```
### 段(Segment)
段是 Lucene 索引的基本单位,是不可变的:
**段的生命周期**:
```
1. 文档写入内存缓冲区
┌─────────────────┐
│ Memory Buffer │ ← 新文档
└─────────────────┘
2. Refresh:内存缓冲区 → 新段(可搜索)
┌─────────────────┐
│ Segment 0 │ ← 可搜索
└─────────────────┘
3. 更多文档写入,创建更多段
┌─────────┐ ┌─────────┐ ┌─────────┐
│Segment 0│ │Segment 1│ │Segment 2│
└─────────┘ └─────────┘ └─────────┘
4. Merge:多个小段合并为大段
┌─────────────────────────────────┐
│ Merged Segment │
└─────────────────────────────────┘
```
**段合并(Merge)**:
```json
// 强制合并(谨慎使用,资源消耗大)
POST /my-index/_forcemerge?max_num_segments=1
```
### _source 字段
`_source` 存储文档的原始 JSON:
```json
// 禁用 _source(节省存储,但无法更新/reindex)
PUT /my-index
{
"mappings": {
"_source": {
"enabled": false
}
}
}
// 部分存储
PUT /my-index
{
"mappings": {
"_source": {
"includes": ["title", "date"],
"excludes": ["content"]
}
}
}
```
### 存储结构总结
```
┌─────────────────────────────────────────────────────────────────┐
│ Lucene Segment │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Inverted Index │ │ Doc Values │ │ _source │ │
│ │ │ │ │ │ │ │
│ │ • Term Dict │ │ • 列式存储 │ │ • 原始 JSON │ │
│ │ • Posting List │ │ • 排序/聚合用 │ │ • 更新/高亮用 │ │
│ │ • 全文搜索用 │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Stored Fields │ │ Term Vectors │ │ Norms │ │
│ │ │ │ │ │ │ │
│ │ • 存储的字段值 │ │ • 词项位置信息 │ │ • 字段长度归一化│ │
│ │ │ │ • 高亮/MLT 用 │ │ • 评分用 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## 分布式架构
### 文档路由
文档通过路由算法分配到特定分片:
```
shard_num = hash(routing) % number_of_primary_shards
```
默认 `routing` 值为文档 `_id`。
```json
// 自定义路由
PUT /products/_doc/1?routing=user123
{
"name": "Product A",
"user_id": "user123"
}
// 查询时指定路由(提高效率)
GET /products/_search?routing=user123
{
"query": {
"match": { "user_id": "user123" }
}
}
```
### 写入流程
```
1. 客户端发送写入请求到协调节点
2. 协调节点根据路由计算目标主分片
3. 请求转发到主分片所在节点
4. 主分片执行写入
5. 主分片并行复制到所有副本分片
6. 所有副本确认后,返回成功响应
┌────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────>│ Coordinating│────>│ Primary │
└────────┘ │ Node │ │ Shard │
└─────────────┘ └──────┬──────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│Replica │ │Replica │ │Replica │
│ 1 │ │ 2 │ │ 3 │
└────────┘ └────────┘ └────────┘
```
### 搜索流程
搜索分为两个阶段:Query Phase 和 Fetch Phase。
**Query Phase**:
```
1. 协调节点将请求发送到所有相关分片
2. 每个分片执行本地搜索,返回匹配文档的 ID 和排序值
3. 协调节点合并结果,确定最终的 Top N 文档
┌────────┐ ┌─────────────┐
│ Client │────>│ Coordinating│
└────────┘ │ Node │
└──────┬──────┘
│ Query
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Shard 0 │ │ Shard 1 │ │ Shard 2 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────┼────────────┘
│ Doc IDs + Scores
▼
┌─────────────┐
│ Coordinating│
│ Node │
└─────────────┘
```
**Fetch Phase**:
```
1. 协调节点向包含目标文档的分片发送 Fetch 请求
2. 分片返回完整文档内容
3. 协调节点组装最终结果返回客户端
┌─────────────┐
│ Coordinating│
│ Node │
└──────┬──────┘
│ Fetch (Doc IDs)
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Shard 0 │ │ Shard 1 │ │ Shard 2 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────┼────────────┘
│ Full Documents
▼
┌─────────────┐
│ Coordinating│──────> Client
│ Node │
└─────────────┘
```
### 集群状态管理
集群状态(Cluster State)包含:
- 节点信息
- 索引元数据
- 分片分配信息
- 映射定义
- 集群设置
```json
// 查看集群状态
GET /_cluster/state
// 查看集群状态大小
GET /_cluster/state?filter_path=metadata.indices.*.state
```
### 主节点选举
Elasticsearch 使用基于 Raft 的选举算法(7.0+):
```
┌─────────────────────────────────────────────────────────┐
│ 主节点选举流程 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 节点启动,进入候选状态 │
│ 2. 候选节点向其他节点请求投票 │
│ 3. 获得多数票的节点成为主节点 │
│ 4. 主节点定期发送心跳 │
│ 5. 主节点失联,重新选举 │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │(Master) │◄───│ │ │ │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ ▲ ▲ │
│ │ 心跳 │ │ │
│ └──────────────┴──────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
```
**防止脑裂**:
```yaml
# 配置初始主节点(仅首次启动时需要)
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]
```
## 本章小结
本章深入介绍了 Elasticsearch 的架构原理:
1. **集群架构**:由协调层、Master 节点、Data 节点、Ingest 节点等组成
2. **节点类型**:
- Master:管理集群状态和元数据
- Data:存储数据,执行搜索和聚合
- Coordinating:路由请求,聚合结果
- Ingest:数据预处理
- ML:机器学习任务
3. **数据存储**:
- 倒排索引:全文搜索的核心
- Doc Values:排序和聚合的列式存储
- 段:不可变的 Lucene 索引单元
4. **分布式机制**:
- 文档路由:hash(routing) % shards
- 写入流程:主分片写入 → 副本复制
- 搜索流程:Query Phase → Fetch Phase
理解这些架构原理有助于更好地设计和优化 Elasticsearch 应用。
## 参考资料
- [Elasticsearch 集群架构](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-cluster.html)
- [节点角色](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html)
- [Lucene 倒排索引](https://lucene.apache.org/core/)
- [分布式搜索执行](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-shard-routing.html)

评论
0暂无评论