Elasticsearch第3章:架构原理

忘机山人

发布于218天前
博客图片修整中,看不了可以先搜索公众号“忘机山人”看。

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)
  • 创建和删除索引
  • 分片分配和重平衡决策
  • 节点加入和离开处理

配置

# elasticsearch.yml
node.roles: [master]

最佳实践

  • 生产环境至少 3 个 Master-eligible 节点
  • 专用 Master 节点不存储数据,资源需求较低
  • 配置 cluster.initial_master_nodes 防止脑裂
# 专用 Master 节点配置
node.roles: [master]
cluster.initial_master_nodes: ["master-1", "master-2", "master-3"]

Data 节点

Data 节点负责数据存储和处理:

职责

  • 存储分片数据
  • 执行 CRUD 操作
  • 执行搜索和聚合

配置

# elasticsearch.yml
node.roles: [data]

数据节点分层(8.x 新特性):

角色说明适用数据
data_hot热数据节点最新、频繁访问的数据
data_warm温数据节点较少访问的历史数据
data_cold冷数据节点很少访问的归档数据
data_frozen冻结数据节点极少访问,可搜索快照
data_content内容数据节点非时序数据
# 热数据节点配置
node.roles: [data_hot]

# 温数据节点配置
node.roles: [data_warm]

Coordinating 节点

协调节点(也称为客户端节点)专门处理请求路由:

职责

  • 接收客户端请求
  • 将请求路由到相关分片
  • 聚合各分片返回的结果
  • 返回最终结果给客户端

配置

# 专用协调节点(不设置任何角色)
node.roles: []

工作流程

客户端请求
    │
    ▼
┌─────────────────┐
│  Coordinating   │
│     Node        │
└────────┬────────┘
         │ 路由请求
    ┌────┴────┬────────┐
    ▼         ▼        ▼
┌───────┐ ┌───────┐ ┌───────┐
│Shard 0│ │Shard 1│ │Shard 2│
└───┬───┘ └───┬───┘ └───┬───┘
    │         │        │
    └────┬────┴────────┘
         │ 聚合结果
         ▼
┌─────────────────┐
│  Coordinating   │
│     Node        │
└────────┬────────┘
         │
         ▼
    客户端响应

Ingest 节点

Ingest 节点在文档索引前执行预处理:

职责

  • 执行 Ingest Pipeline
  • 数据转换和丰富
  • 字段提取和格式化

配置

# elasticsearch.yml
node.roles: [ingest]

Pipeline 示例

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 任务:

职责

  • 异常检测作业
  • 预测分析
  • 数据帧分析

配置

# elasticsearch.yml
node.roles: [ml]
xpack.ml.enabled: true

节点角色组合建议

小型集群(开发/测试)

# 所有节点都是全角色
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 ValuesFielddata
存储位置磁盘内存
构建时机索引时查询时
适用类型keyword, numeric, date, ip, geotext
内存占用
默认状态启用禁用
// 禁用 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)

// 强制合并(谨慎使用,资源消耗大)
POST /my-index/_forcemerge?max_num_segments=1

_source 字段

_source 存储文档的原始 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

// 自定义路由
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)包含:

  • 节点信息
  • 索引元数据
  • 分片分配信息
  • 映射定义
  • 集群设置
// 查看集群状态
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) │◄───│         │    │         │             │
│  └─────────┘    └─────────┘    └─────────┘             │
│       │              ▲              ▲                   │
│       │   心跳       │              │                   │
│       └──────────────┴──────────────┘                   │
│                                                         │
└─────────────────────────────────────────────────────────┘

防止脑裂

# 配置初始主节点(仅首次启动时需要)
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 应用。

参考资料

image.png

评论

0

暂无评论

说点什么呢~
收藏
0
0
0