这是《Elasticsearch 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Elasticsearch 是分布式系统,但这不意味着“加节点就一定快”。真正决定集群能力和稳定性的,是节点角色、分片数量、分片大小、副本、分配策略和查询扇出。
本章从架构视角拆解 Elasticsearch 集群。
23.1 集群组成
一个最小生产集群通常包含:
- 3 个 master-eligible 节点;
- 若干 data 节点;
- 协调节点或负载均衡入口;
- Kibana / 监控组件;
- 快照存储仓库。
flowchart TB
C[Client] --> L[Load Balancer]
L --> N1[Coordinating Node]
N1 --> D1[Data Node 1]
N1 --> D2[Data Node 2]
N1 --> D3[Data Node 3]
M1[Master-eligible 1] -. cluster state .-> N1
M2[Master-eligible 2] -. vote .-> M1
M3[Master-eligible 3] -. vote .-> M1
集群状态由 master 节点维护,包括索引、分片、节点、别名、模板和分配信息。cluster state 越大,发布和同步成本越高,这是“不要创建几十万小索引”的重要原因。
23.2 节点角色
查看节点:
GET /_cat/nodes?v&h=name,node.role,master,heap.percent,ram.percent,cpu,load_average,disk.used_percent
常见角色:
| 角色 | 配置 | 职责 |
|---|---|---|
| master-eligible | node.master: true |
参与选主,维护集群状态 |
| data | node.data: true |
存储分片,执行读写 |
| data_hot | node.attr.data: hot |
承接高频写入和查询 |
| data_warm | node.attr.data: warm |
承接较旧、低频查询 |
| data_cold | node.attr.data: cold |
承接冷数据 |
| data_content | 内容数据层 | 常规业务索引 |
| ingest | node.ingest: true |
执行 ingest pipeline |
| coordinating only | 所有 master/data/ingest 为 false | 接收请求、分发归并 |
| machine learning | node.ml: true |
机器学习作业 |
节点角色不是越多越好。小集群可以混合部署,大集群应分离 master、data 和 coordinating,避免 master 被重查询或重写入拖垮。
23.3 Master 与选主
master-eligible 节点通过投票选出 active master。为了避免脑裂,必须满足:
master-eligible 数量 >= 2 * 失败节点数 + 1
常见选择:
| 节点数 | 最多容忍失败数 | 说明 |
|---|---|---|
| 1 | 0 | 无高可用,仅开发环境 |
| 2 | 0 | 一台失败就可能无法选出 master |
| 3 | 1 | 生产最小推荐 |
| 5 | 2 | 大集群常用 |
master 做的事:
- 维护 cluster state;
- 创建、删除索引;
- 分配和迁移分片;
- 处理节点加入和离开;
- 发布集群状态变更。
master 不直接执行搜索,但集群状态过大会影响所有节点。应避免:
- 无限创建小索引;
- 每日索引分片数过大;
- 大量无用别名和模板;
- 频繁批量创建删除索引;
- 让 master 节点同时承担重 data 负载。
查看当前 master:
GET /_cluster/state/master_node
查看未选举原因:
GET /_cluster/health?explain=true
23.4 分片与路由
创建索引时必须认真设置 number_of_shards,主分片数一旦创建不能直接修改:
PUT orders-v4
{
"settings": {
"number_of_shards": 6,
"number_of_replicas": 1
}
}
文档路由公式:
shard = hash(routing) % number_of_primary_shards
默认 routing 是文档 _id。这意味着:
- 主分片数改变后,同一文档会路由到不同分片;
- 不能通过 update setting 直接修改主分片数;
- 需要变更主分片数时,只能 reindex 到新索引;
- 自定义 routing 可以让同一类文档落同一分片,但可能造成数据倾斜。
23.4.1 分片不是越多越好
分片多的好处:
- 数据分布更均匀;
- 查询可以并行; -单分片恢复更快。
分片多的代价:
- 查询扇出更大;
- 每个分片都是一个 Lucene 实例,内存和句柄更多;
- 合并和刷新任务更多;
- cluster state 更大;
- 小分片浪费磁盘和 CPU。
经验边界:
- 单分片常见容量:10GB 到 50GB;
- 日志类可以放宽到 50GB 左右;
- 搜索类和频繁更新类建议更小;
- 面向低延迟查询的索引不宜使用几十上百个分片;
- 用 rollover 按主分片大小滚动,而不是只按时间滚动。
23.4.2 副本
副本解决高可用和读吞吐:
PUT orders-v4/_settings
{
"number_of_replicas": 2
}
含义:
- 每个主分片有 2 个副本分片;
- 总数据分片数 = 主分片数 * (1 + 副本数);
- 写入需要同步主副本;
- 查询可以落在主或副本。
副本不是备份。节点损坏后副本能参与恢复,但误删除索引、错误脚本和逻辑损坏仍会同步到副本。真正的备份依赖 snapshot。
23.5 分片分配
分片分配由 master 决定。常见影响因素:
| 因素 | 说明 |
|---|---|
| 节点在线状态 | 节点离线后分片需要重新分配 |
| 磁盘水位 | 低磁盘水位可能阻止分配和写入 |
| 副本数 | 副本必须与主分片不同节点 |
| allocation filtering | 按节点属性迁移分片 |
| awareness | 机架或区域感知 |
| shard balancing | 节点间分片数量和磁盘均衡 |
| recovery 限流 | 恢复速度受网络和磁盘限流控制 |
查看未分配原因:
GET /_cluster/allocation/explain
查看分片:
GET /_cat/shards/orders-v4?v&h=index,shard,prirep,state,docs,store,node,unassigned.reason
23.5.1 磁盘水印
常见默认值:
| 水位 | 默认 | 行为 |
|---|---|---|
| low | 85% | 不再向该节点分配新分片 |
| high | 90% | 尝试迁移该节点分片 |
| flood_stage | 95% | 索引标记 read-only,阻止写入 |
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.disk.watermark.low": "85%",
"cluster.routing.allocation.disk.watermark.high": "90%",
"cluster.routing.allocation.disk.watermark.flood_stage": "95%"
}
}
如果触发 flood stage,需要先清理空间或扩容,再手动解除 read-only:
PUT /_all/_settings
{
"index.blocks.read_only_allow_delete": null
}
解除锁只是止血,不是治理。根因通常是分片过多、保留期过长、副本过多或容量规划不足。
23.6 集群健康
GET /_cluster/health?wait_for_status=yellow&timeout=10s
| 状态 | 含义 |
|---|---|
| green | 主分片和副本都正常 |
| yellow | 主分片正常,至少一个副本未分配 |
| red | 至少一个主分片不可用 |
查看具体问题:
GET /_cat/indices?v&health=red
GET /_cat/shards?v&h=index,shard,prirep,state,node,unassigned.reason
GET /_cluster/allocation/explain
yellow 不一定立刻影响查询,但表示高可用受损。red 则表示部分数据不可读写,应按索引和分片定位。
23.7 冷热架构
日志和指标类数据常用冷热分层:
node.attr.data: hot
node.roles: [data_hot]
node.attr.data: warm
node.roles: [data_warm]
索引模板可以指定层:
PUT _component_template/logs-allocate
{
"template": {
"settings": {
"index.routing.allocation.include._tier_preference": "data_hot"
}
}
}
Hot 节点:
- SSD;
- 更高 CPU 和内存;
- 承接写入和最近查询;
- 更高副本数。
Warm 节点:
- 大容量磁盘;
- 较低 CPU;
- 查询频率较低;
- 可减少副本或使用压缩。
Cold 节点:
- searchable snapshot 或归档快照;
- 查询延迟容忍度高;
- 成本优先。
分层的关键是数据时间边界清晰,并且 ILM 能自动 rollover 和迁移。
23.8 查询扇出
一次搜索请求默认会路由到每个涉及索引的每个分片,每个分片再选择一个主或副本副本执行。
6 个索引 x 10 主分片 x 1 副本 = 60 个目标分片
查询扇出过大时:
- 协调节点内存和 CPU 上升;
- 每个分片结果队列占用内存;
- 尾部延迟变差;
- 集群整体吞吐下降;
- 低效查询被放大。
治理方式:
- 缩小索引模式范围;
- 使用别名精确指向索引;
- 查询必须带时间过滤并路由到相关时间分区;
- 控制分片数量;
- 使用 pre-filter shard 能力;
- 大范围分析迁移预聚合或离线集群;
- 避免通配符查询所有数据。
23.9 扩容方式
Elasticsearch 扩容分两类:
23.9.1 增加副本
适合读压力大、数据量不变的场景。副本增加会提高磁盘占用和写入成本。
23.9.2 增加数据节点
适合磁盘或 CPU 压力大。已有主分片数不变,但集群会重新平衡分片。
如果主分片数太少,例如只有 1 个,增加节点无法让写入并行拆分,只能通过 reindex 新建更多分片。
扩容步骤:
- 确认瓶颈是磁盘、CPU、页缓存、IO 还是查询扇出;
- 预估新节点后的分片分布;
- 调整 allocation 限流,避免恢复打满网络;
- 低峰加入节点;
- 观察恢复速度、磁盘水位和查询延迟;
- 确认未出现新热点节点。
临时禁用分配:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": "none"
}
}
恢复分配:
PUT /_cluster/settings
{
"persistent": {
"cluster.routing.allocation.enable": null
}
}
23.10 架构设计检查清单
- master-eligible 节点数量为 3 或 5,且不承担重数据负载;
- 每个索引的主分片数有容量依据;
- 单分片大小控制在合理范围;
- 副本数与可用性要求匹配;
- 日志和时序数据有 ILM 和 rollover;
- 查询无法随意扫全量索引;
- 冷热节点属性和 tier preference 正确;
- 磁盘水位告警早于 flood stage;
- 快照仓库可用且定期恢复演练;
- cluster state、open indices、shard count 有上限治理。
本章小结
分片架构决定了 Elasticsearch 的容量上限和故障行为。主分片数一旦确定就难以修改,分片过多会放大查询扇出和集群状态成本,分片过少又会限制并行度和恢复能力。生产设计要从数据增长、查询模式、副本目标和运维成本四方面同时评估。
思考题
- 为什么主分片数不能通过设置直接修改?
- 3 个 master-eligible 节点和 2 个节点有什么本质差别?
- yellow 和 red 分别代表什么风险?
- 什么情况下增加副本有效,什么情况下应该 reindex?
- 查询 30 天日志很慢,分片架构上有哪些可能原因?