Elasticsearch 集群深度解析
从入门到生产级别的完整指南
一、集群的基本概念
Elasticsearch 集群(Cluster)是由一个或多个节点(Node)组成的分布式系统,这些节点共同持有全部数据,并提供跨所有节点的联合索引和搜索能力。
1.1 核心组成元素
| 概念 | 说明 |
|---|---|
| 集群(Cluster) | 多个节点的集合,共享同一个 cluster.name,对外表现为统一的服务入口 |
| 节点(Node) | 集群中的单个 ES 实例,每个节点有唯一的 node.name 和 UUID |
| 索引(Index) | 类似关系型数据库中的"数据库",是文档的逻辑命名空间 |
| 分片(Shard) | 索引被切分成多个分片分布在不同节点上,分为主分片(Primary)和副本分片(Replica) |
| 文档(Document) | 存储的最小单元,以 JSON 格式表示 |
1.2 节点角色类型
在一个生产级别的集群中,节点通常承担不同的职责:
┌─────────────────────────────────────────────────────┐
│ ES 集群架构 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Master Node │ │ Master Node │ (主节点选举) │
│ │ (Active) │◄──►│ (Standby) │ │
│ └──────┬───────┘ └──────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────┐ │
│ │ 协调层 │ │
│ │ Coordinating Node │ │
│ └──────┬──────────────────────┬───────┘ │
│ │ │ │
│ ┌──────▼───────┐ ┌─────────▼──────┐ │
│ │ Data Node 1 │ │ Data Node 2 │ (数据层) │
│ │ [Shard 0,1] │ │ [Shard 2,3] │ │
│ └──────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────┘
- Master Node(主节点):负责集群管理,如创建/删除索引、追踪节点加入/离开。不存储数据。
- Data Node(数据节点):负责存储数据、执行 CRUD、搜索和聚合操作,资源消耗大。ES 9.x 中进一步细分为数据分层角色:
data_content:通用数据节点,存储非时序数据data_hot:热层节点,存储最新、高频访问的时序数据data_warm:温层节点,存储访问频率下降的近期数据data_cold:冷层节点,存储较少访问的历史数据data_frozen:冻结层节点,存储极少访问的归档数据
- Coordinating Node(协调节点):接收客户端请求,将请求路由到正确的数据节点,并汇总结果。
- Ingest Node(摄取节点):在文档索引前执行预处理管道(Pipeline),类似轻量级 Logstash。
- ML Node(机器学习节点):运行机器学习任务(需要白金版许可)。
1.3 分片与副本
索引:my_index(3 个主分片,1 个副本)
Node 1 Node 2 Node 3
┌──────────┐ ┌──────────┐ ┌──────────┐
│ P0 │ R1 │ │ P1 │ R2 │ │ P2 │ R0 │
└──────────┘ └──────────┘ └──────────┘
主0 副1 主1 副2 主2 副0
- 主分片数在索引创建时确定,不可更改(可通过
_split/_shrink变更) - 副本数可随时动态调整
- 副本分片永远不会和对应的主分片在同一节点上
二、集群 vs 单体对比
2.1 全面对比
| 维度 | 单节点(单体) | 集群(多节点) |
|---|---|---|
| 高可用性 | ❌ 单点故障,宕机即服务中断 | ✅ 节点故障自动转移,服务持续可用 |
| 存储容量 | 受单机磁盘限制 | 水平扩展,理论无上限 |
| 查询性能 | 单节点并发处理 | 并行分布式查询,性能随节点线性扩展 |
| 写入吞吐 | 受单节点 I/O 瓶颈限制 | 多节点并行写入,吞吐倍增 |
| 数据安全 | 无冗余,磁盘损坏即丢失 | 副本机制,数据多副本保障 |
| 运维复杂度 | 低,配置简单 | 较高,需关注网络、选举、分片均衡 |
| 成本 | 低 | 较高(多台服务器) |
| 适用场景 | 开发/测试环境 | 生产环境 |
三、自动发现(Auto Discovery)
ES 集群通过**发现机制(Discovery)**让各节点自动找到彼此并组建集群,无需手动指定集群拓扑。
3.1 发现机制演进
| 版本 | 发现模块 | 说明 |
|---|---|---|
| ES 6.x 及以前 | Zen Discovery | 基于广播/单播的发现机制 |
| ES 7.0+ | Zen2(默认) | 全新实现,更稳定的主节点选举 |
| ES 9.x | Zen2(增强版) | 在 Zen2 基础上优化了选举性能和稳定性 |
3.2 发现流程
节点启动
│
▼
读取 seed_hosts 列表
│
▼
向种子节点发起 Ping 请求
│
▼
收集集群成员信息
│
▼
参与主节点选举(投票)
│
├─── 已有 Master ──► 加入现有集群
│
└─── 无 Master ────► 发起选举,获得多数票的节点成为 Master
3.3 主节点选举(Quorum 机制)
ES 7.x 使用 Raft 算法的变体进行选举,核心原则是过半数胜出:
集群节点数 最少 Master 候选数 可容忍宕机数
1 1 0
2 2 0(不推荐)
3 2 1 ✅ 推荐最小集群
5 3 2
7 4 3
⚠️ 脑裂(Split Brain):网络分区时,两个子集群各自选出 Master,导致数据不一致。 解决方案:确保
minimum_master_nodes = (master候选数 / 2) + 1
3.4 发现配置示例
# elasticsearch.yml
# 种子节点列表(其他节点的地址,用于初始发现)
discovery.seed_hosts:
- "es-node-1:9300"
- "es-node-2:9300"
- "es-node-3:9300"
# 初始主节点候选列表(仅首次集群引导时使用)
cluster.initial_master_nodes:
- "node-1"
- "node-2"
- "node-3"
3.5 云环境中的自动发现
在 Kubernetes 或云环境中,可以使用插件实现动态发现:
# 使用 ec2 发现插件(AWS)
discovery.ec2.tag.Environment: production
# 使用 k8s 服务发现
discovery.seed_providers: file
四、集群的核心配置
4.1 elasticsearch.yml 关键配置
# ======================== 集群身份 ========================
cluster.name: my-production-cluster # 同一集群的所有节点必须一致
node.name: node-1 # 节点唯一名称,建议有语义
# ======================== 节点角色 ========================
# ES 9.x 推荐按角色分离部署
# 专用 Master 节点配置
# node.roles: [ master ]
# 热层数据节点配置
# node.roles: [ data_hot, data_content ]
# 温层数据节点配置
# node.roles: [ data_warm, data_content ]
# 冷层数据节点配置
# node.roles: [ data_cold, data_content ]
# 协调节点配置(不存储数据,不参与选举)
# node.roles: []
# 小型集群可合并角色(不推荐生产环境)
node.roles:
- master
- data_hot
- data_content
- ingest
# ======================== 网络配置 ========================
network.host: 0.0.0.0 # 监听所有网卡(生产环境指定具体 IP)
http.port: 9200 # 客户端 REST API 端口
transport.port: 9300 # 节点间通信端口
# ======================== 发现配置 ========================
discovery.seed_hosts:
- "192.168.1.10:9300"
- "192.168.1.11:9300"
- "192.168.1.12:9300"
cluster.initial_master_nodes:
- "node-1"
- "node-2"
- "node-3"
# ======================== 数据路径 ========================
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
# ======================== 内存锁定 ========================
bootstrap.memory_lock: true # 防止 JVM 内存被 swap 到磁盘
# ======================== 安全配置(ES 9.x) ========================
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
4.2 JVM 内存配置(jvm.options)
# 规则:堆内存设为物理内存的 50%,且不超过 31GB
-Xms16g
-Xmx16g # Xms 和 Xmx 必须相同,避免运行时动态扩缩堆
# ES 9.x 默认使用 G1GC,无需显式配置
# 如需调整 G1 参数(可选):
# -XX:G1ReservePercent=25
# -XX:InitiatingHeapOccupancyPercent=30
4.3 索引级别的核心配置
PUT /my_index
{
"settings": {
"number_of_shards": 1, // ES 8.x+ 默认 1 主分片(创建后不可改)
"number_of_replicas": 1, // ES 8.x+ 默认 1 副本(可动态调整)
"refresh_interval": "30s", // 写入性能优化:降低刷新频率
"index.routing.allocation.include._tier_preference": "data_hot,data_warm,data_cold" // ES 9.x 数据分层
}
}
4.4 分片规划建议
分片大小建议:20GB ~ 50GB / 个分片
计算公式:
主分片数 = ceil(预估总数据量 / 40GB)
示例:
预估数据量 = 500GB
主分片数 = ceil(500 / 40) = 13 个
副本数 = 1(生产最少一个副本)
总分片 = 13 × 2 = 26 个
4.5 系统级配置(Linux)
# /etc/sysctl.conf
vm.max_map_count=262144 # 虚拟内存映射数,ES 强制要求
vm.swappiness=1 # 尽量不使用 swap
# /etc/security/limits.conf
elasticsearch soft nofile 65536 # 文件描述符限制
elasticsearch hard nofile 65536
elasticsearch soft nproc 4096 # 线程数限制
五、集群的健康值状态
5.1 三种健康状态
🟢 GREEN 所有主分片和副本分片均已分配,集群完全健康
🟡 YELLOW 所有主分片已分配,但至少一个副本分片未分配(数据安全但存在单点风险)
🔴 RED 至少有一个主分片未分配,部分数据不可访问
5.2 查看集群健康
# 快速查看
GET /_cluster/health
# 返回示例
{
"cluster_name": "my-production-cluster",
"status": "green", # 健康状态
"number_of_nodes": 3, # 节点总数
"number_of_data_nodes": 3, # 数据节点数
"active_primary_shards": 15, # 活跃主分片
"active_shards": 30, # 活跃分片总数(含副本)
"relocating_shards": 0, # 正在迁移的分片
"initializing_shards": 0, # 正在初始化的分片
"unassigned_shards": 0, # 未分配分片(非0则告警)
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"active_shards_percent_as_number": 100.0
}
5.3 各状态场景分析
场景 1:新增副本,节点数不足
→ 状态变为 YELLOW
→ 副本无法和主分片在同一节点
→ 解决:增加节点 或 减少副本数
场景 2:数据节点宕机
→ 立即变为 YELLOW 或 RED
→ ES 默认等待 1 分钟后开始重新分配
→ 恢复:节点重启(分片自动恢复)或 触发分片重分配
场景 3:磁盘水位线触发
→ 节点磁盘 > 85% → 停止分配新分片到该节点(YELLOW)
→ 节点磁盘 > 90% → 已有分片开始迁移出该节点
→ 节点磁盘 > 95% → 索引被设为只读(RED)
5.4 监控关键指标
# 节点级别的详细指标
GET /_nodes/stats
# 索引级别健康
GET /_cat/indices?v&health=yellow
# 分片分配状态
GET /_cat/shards?v
# 未分配分片原因
GET /_cluster/allocation/explain
六、分片的故障诊断
当集群健康状态为 YELLOW 或 RED 时,通常意味着存在未分配的分片。使用 /_cluster/allocation/explain API 可以快速定位问题根因。
6.1 使用 explain 诊断分片问题
# 查看任意未分配分片的详情(不指定具体分片)
GET /_cluster/allocation/explain
# 查看特定索引的特定分片
GET /_cluster/allocation/explain
{
"index": "my_index",
"shard": 0,
"primary": true
}
6.2 explain 返回结果解读
{
"index": "my_index",
"shard": 0,
"primary": true,
"current_state": "unassigned", // 当前状态
"unassigned_info": {
"reason": "NODE_LEFT", // 未分配原因
"at": "2024-05-11T10:30:00Z",
"details": "node left the cluster"
},
"can_allocate": "no", // 是否可以分配
"allocate_explanation": "cannot allocate because no node has enough disk space",
"node_allocation_decisions": [
{
"node_name": "node-1",
"deciders": [
{
"decider": "disk_threshold",
"decision": "NO",
"explanation": "the node is above the low watermark"
}
]
}
]
}
6.3 常见的未分配原因
| reason 值 | 含义 | 处理方案 |
|---|---|---|
NODE_LEFT | 节点离开集群 | 恢复节点或等待分片重新分配 |
ALLOCATION_FAILED | 分配失败次数超限 | 检查节点资源,手动重试 |
INDEX_CREATED | 新索引,等待分配 | 正常现象,等待分配完成 |
DISK_WATERMARK | 磁盘超水位 | 清理磁盘或扩容 |
CLUSTER_RECOVERED | 集群恢复中 | 等待恢复完成 |
6.4 强制重新分配分片
# 谨慎使用:强制分配主分片(可能丢失数据)
POST /_cluster/reroute
{
"commands": [{
"allocate_stale_primary": {
"index": "my_index",
"shard": 0,
"node": "node-1",
"accept_data_loss": true
}
}]
}
# 强制分配副本分片(风险较低)
POST /_cluster/reroute
{
"commands": [{
"allocate_replica": {
"index": "my_index",
"shard": 0,
"node": "node-1"
}
}]
}
总结
集群搭建核心原则:
✅ Master 节点数量为奇数(3 / 5 / 7)
✅ 主分片数在规划阶段确定,预留增长空间
✅ 堆内存 ≤ 物理内存 50%,且 ≤ 31GB
✅ 生产环境至少 1 个副本
✅ 开启磁盘水位线告警,提前处理
✅ 定期检查集群健康状态与慢查询日志
✅ 数据节点与主节点角色分离(规模较大时)
