Elasticsearch 学习笔记(五):集群与运维

写在前面

本文是 Elasticsearch 学习笔记系列的最后一篇,介绍集群架构、节点角色、分片分配、索引生命周期管理和运维监控。前置知识:进阶查询与优化(第四篇)。


一、集群架构

1.1 节点角色

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Master 节点(master)
  - 管理集群元数据(索引、Mapping、分片位置)
  - 不参与数据存储和查询
  - 生产环境建议 3 个专用 Master 节点(避免脑裂)

Data 节点(data)
  - 存储数据和执行查询
  - 最核心的节点,IO 和 CPU 消耗最大
  - 根据数据量增加 Data 节点

Coordinating 节点
  - 接收客户端请求,分发到 Data 节点,汇总返回
  - 不存储数据,不做 Master
  - 适合做查询入口,减轻 Data 节点压力

Ingest 节点(ingest)
  - 数据预处理(Pipeline)
  - 类似 Logstash 的功能

ML 节点(ml)
  - 机器学习任务(异常检测等)

1.2 最小生产集群

1
2
3
3 Master 节点   — 高可用(容忍 1 个故障,剩余 2 个仍构成多数派)
3 Data 节点     — 数据存储和查询(副本需要 3 节点)
2 协调节点      — 查询入口(可选)

1.3 配置节点角色

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# elasticsearch.yml

# Master 节点
node.roles: [master]

# Data 节点
node.roles: [data]

# 协调节点
node.roles: []

# Data + Master(小规模,合一部署)
node.roles: [data, master]

# Data Hot / Warm / Cold(冷热分层)
node.roles: [data_hot]
node.roles: [data_warm]
node.roles: [data_cold]

二、分片和副本

2.1 分片分配

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
创建索引时指定分片数:
  PUT /products
  {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1
    }
  }

  3 个主分片 + 3 个副本分片 = 6 个分片

  分片分配规则:
  - 主分片和副本不在同一节点
  - 尽量均匀分布到各节点
  - 主分片数不能直接原地修改;可按约束使用 split、shrink,或 reindex 到新索引

2.2 副本的作用

1
2
3
4
5
6
7
1. 高可用    — 主分片挂了,副本自动提升为主
2. 负载均衡  — 查询可以命中主分片或副本,增加集群的读服务能力
3. 容灾      — 在副本已分配且故障数量未超过副本能力时保护数据可用性

副本数可以动态调整:
  PUT /products/_settings
  { "number_of_replicas": 2 }

2.3 分片数规划

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
经验值:
  分片大小应按节点资源、查询延迟、恢复时间和数据保留策略压测确定;不存在通用固定区间

计算:
  例如数据量 100GB、初步目标每分片约 30GB,可从 4 个主分片开始压测
  3 节点集群 → 每个节点约 1-2 个主分片 + 副本

避免:
  - 过多小分片会增加集群状态、堆内存和调度开销
  - 过大的分片会拉长恢复、迁移和部分查询时间

三、分片分配策略

3.1 分片过滤

1
2
3
4
5
6
7
8
9
// 节点级别打标签
// elasticsearch.yml
// node.attr.box_type: hot

// 索引分配到指定节点
PUT /products/_settings
{
  "index.routing.allocation.require.box_type": "hot"
}

3.2 冷热架构

1
2
3
4
5
6
7
8
9
// Hot 节点:SSD,存储近期热数据
// Warm 节点:HDD,存储较冷数据
// Cold 节点:低成本存储,归档数据

// 索引从热迁移到温
PUT /products-2026-03/_settings
{
  "index.routing.allocation.require.box_type": "warm"
}

四、索引生命周期管理(ILM)

4.1 ILM 策略

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
PUT /_ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_primary_shard_size": "30gb",
            "max_age": "7d"
          },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 },
          "allocate": {
            "require": { "box_type": "warm" }
          },
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": { "box_type": "cold" }
          },
          "set_priority": { "priority": 0 }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
生命周期阶段:
  hot     — 活跃数据,频繁读写,SSD 存储
  warm    — 较少查询,只读,HDD 存储
  cold    — 极少查询,低成本存储
  delete  — 过期删除

触发条件(rollover):
  max_primary_shard_size  — 主分片达到大小
  max_age                — 索引存在时间
  max_docs               — 文档数量

自动操作:
  rollover     — 滚动创建新索引
  shrink       — 减少分片数(冷数据不需要多分片)
  forcemerge   — 合并 Segment(减少文件数)
  allocate     — 迁移到指定节点
  delete       — 删除索引

4.2 使用 ILM

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 索引模板中关联 ILM 策略
PUT /_index_template/logs_template
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs-policy",
      "index.lifecycle.rollover_alias": "logs"
    }
  }
}

// 创建第一个索引
PUT /logs-000001
{
  "aliases": {
    "logs": { "is_write_index": true }
  }
}
// 写入 logs 别名 → 自动写入 logs-000001
// 主分片达 30GB 或索引满 7 天 → rollover 创建 logs-000002 并切换写入
// 索引 7 天后 → 迁移到 warm 节点
// 30 天后 → 迁移到 cold 节点
// 90 天后 → 自动删除

五、集群监控

5.1 集群健康

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 集群整体健康
GET /_cluster/health
// status: green / yellow / red
// green  — 所有主分片和副本都正常
// yellow — 所有主分片正常,部分副本缺失
// red    — 部分主分片不可用(数据丢失风险)

// 节点状态
GET /_cat/nodes?v
// 显示:name, ip, heap.percent, ram.percent, cpu, load_1m, node.role

// 分片状态
GET /_cat/shards?v
// 显示哪些分片在哪个节点,是否正常

// 未分配分片
GET /_cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state

5.2 索引监控

1
2
3
4
5
6
7
8
// 索引大小和文档数
GET /_cat/indices?v&h=index,docs.count,store.size,pri.store.size&s=store.size:desc

// 索引健康
GET /_cluster/health?level=indices

// 分片详情
GET /_cat/shards/products?v

5.3 性能指标

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 搜索性能
GET /_stats/search?human

// 索引性能
GET /_stats/indexing?human

// 合并性能
GET /_stats/merges?human

// 段信息
GET /products/_segments

5.4 慢查询日志

1
2
3
4
5
6
7
8
9
// 开启慢查询日志
PUT /products/_settings
{
  "index.search.slowlog.threshold.query.warn": "5s",
  "index.search.slowlog.threshold.query.info": "2s",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.indexing.slowlog.threshold.index.warn": "10s"
}
// 超过阈值的查询会记录到慢查询日志

六、备份和恢复

6.1 创建快照仓库

文件系统仓库要求先在每个 master 与 data 节点的 elasticsearch.yml 中配置共享路径,例如 path.repo: ["/data/es_backup"],并重启节点后再注册仓库。

1
2
3
4
5
6
7
8
// 注册仓库(文件系统)
PUT /_snapshot/my_backup
{
  "type": "fs",
  "settings": {
    "location": "/data/es_backup"
  }
}

6.2 创建快照

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 备份指定索引
PUT /_snapshot/my_backup/snapshot_20260530
{
  "indices": "products,orders",
  "ignore_unavailable": true,
  "include_global_state": false
}

// 备份所有索引
PUT /_snapshot/my_backup/snapshot_all

// 查看快照进度
GET /_snapshot/my_backup/snapshot_20260530/_status

// 查看所有快照
GET /_cat/snapshots/my_backup?v

6.3 恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 从快照恢复
POST /_snapshot/my_backup/snapshot_20260530/_restore
{
  "indices": "products",
  "index_settings": {
    "index.number_of_replicas": 1
  }
}

// 恢复到新索引名
POST /_snapshot/my_backup/snapshot_20260530/_restore
{
  "indices": "products",
  "rename_pattern": "(.+)",
  "rename_replacement": "$1_restored"
}
// 恢复为 products_restored

七、常见运维操作

7.1 节点维护

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 排空节点上的分片(维护前)
PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.exclude._ip": "192.168.1.100"
  }
}
// ES 会自动将该节点上的分片迁移到其他节点
// 迁移完成后可以安全停机

// 维护完成后恢复
PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.exclude._ip": null
  }
}

7.2 索引操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 强制合并段(减少 Segment 数量,降低资源消耗)
POST /products/_forcemerge?max_num_segments=1
// 适合只读索引

// 清理缓存
POST /products/_cache/clear

// 刷新
POST /products/_refresh

// 刷盘
POST /products/_flush

7.3 Reindex(重建索引)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 从旧索引重建到新索引(修改 Mapping 后常用)
POST /_reindex
{
  "source": { "index": "products-v1" },
  "dest":   { "index": "products-v2" }
}

// 带查询条件重建
POST /_reindex
{
  "source": {
    "index": "products-v1",
    "query": {
      "range": { "created_at": { "gte": "2026-01-01" } }
    }
  },
  "dest": { "index": "products-v2" }
}

// 远程集群重建(迁移数据)
POST /_reindex
{
  "source": {
    "remote": {
      "host": "http://old-cluster:9200"
    },
    "index": "products"
  },
  "dest": { "index": "products" }
}

八、常见问题排查

8.1 集群 Red

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
1. 查看哪个索引有问题
   GET /_cluster/health?level=indices

2. 查看未分配的分片
   GET /_cat/shards?v&h=index,shard,prirep,state,unassigned.reason

3. 查看未分配原因
   GET /_cluster/allocation/explain

4. 常见原因:
   - 节点宕机
   - 磁盘满了(水位超过 flood-stage)
   - 副本数超过节点数

8.2 磁盘水位

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
ES 磁盘水位阈值:
  cluster.routing.allocation.disk.watermark.low:    85%(默认)
     不再分配新分片到该节点

  cluster.routing.allocation.disk.watermark.high:    90%(默认)
     开始迁移该节点上的分片

  cluster.routing.allocation.disk.watermark.flood_stage: 95%(默认)
     在超过水位的节点上拥有分片的索引会被设置 `read_only_allow_delete` 写入块

恢复方法:
  1. 清理磁盘空间
  2. 解除只读
     PUT /products/_settings
     { "index.blocks.read_only_allow_delete": null }

8.3 性能慢

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
排查思路:
1. 是写入慢还是查询慢?
   GET /_stats/indexing,search?human

2. 查看慢查询日志
   检查 ES 日志目录中的慢查询记录

3. 检查 JVM 堆内存
   GET /_cat/nodes?v&h=name,heap.percent,ram.percent,cpu
   堆使用持续偏高时,应结合 GC、分配率和查询负载定位原因,再考虑优化查询、调整堆或扩容

4. 检查 GC 情况
   GET /_nodes/stats/jvm?filter_path=*.jvm.gc

5. 检查线程池
   GET /_cat/thread_pool?v&h=node_name,name,active,rejected,completed
   rejected > 0 → 请求被拒绝,需要优化或扩容

九、系列总结

9.1 知识体系回顾

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
第一篇:基础入门
  - 核心概念、倒排索引、索引管理、Mapping

第二篇:文档操作与搜索
  - CRUD、Bulk、全文搜索、精确搜索、bool 复合查询

第三篇:聚合分析
  - 指标聚合、桶聚合、嵌套聚合、实战场景

第四篇:进阶查询与优化
  - 深度分页、分词器、中文分词、评分调优、性能优化

第五篇:集群与运维
  - 节点角色、分片分配、ILM、监控、备份恢复

9.2 核心要点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
1. ES 的核心是倒排索引,理解它就理解了搜索的本质
2. Mapping 要提前设计,生产环境不要依赖动态映射
3. text 字段做全文搜索,keyword 字段做精确匹配和聚合
4. 不需要相关性评分的精确条件放在 filter context;是否缓存和收益由查询形态与负载决定
5. 中文分词器应按词典、召回率、维护能力和目标 ES 版本选择,IK 只是可选方案之一
6. 深分页优先使用 PIT + search_after;批量遍历按官方建议选择 PIT/search_after 或适用的导出机制
7. 日志类数据用 ILM 自动管理生命周期
8. 分片大小没有通用固定值,应按数据量、恢复时间、节点资源和实际查询压测规划
9. 生产集群通常部署 3 个专用 master-eligible 节点;可用性仍取决于故障域、投票配置和运维方式
10. 磁盘水位和 JVM 堆内存是最常出问题的地方