主题
ES 架构与核心概念
一句话:ES 把索引切成主分片分散到多个节点,用副本换高可用和读吞吐;主分片数在创建时固定,所以分片规划是一次性决策,后面只能 reindex、split 或 shrink。
适用版本:Elasticsearch 8.x(8.0 起 type 已移除;7.x 的很多配置写法不再适用)。
为什么需要
单机 Lucene 的瓶颈很直接:索引越大,查询和恢复越慢;机器一坏,数据就不可用。ES 的解法是把一个索引切成多个 primary shard,每个 shard 是一个独立的 Lucene 实例,分散到不同节点;每个 primary 再配 replica,负责故障接管和读扩展。
代价是:分片数决定路由,主分片数不能改;分片太多会拖垮 master 和集群状态,分片太大则恢复慢、单点压力高。架构篇的核心不是背概念,而是把分片数量和大小算清楚。
核心机制
节点角色
8.x 用 node.roles 显式声明角色:
| 角色 | 职责 | 生产建议 |
|---|---|---|
master | 集群状态、索引创建、分片分配 | 3 个专用候选节点,奇数 |
data_hot / data_content | 热数据,频繁读写 | 主力数据节点 |
data_warm / data_cold / data_frozen | 分层存储 | 按生命周期使用 |
ingest | 写入前预处理 | 可与 data 混部,重管道单独部署 |
coordinating(不配 data/master) | 请求路由、结果归并 | 大集群可独立 |
每个节点都能做 coordinating;专用 coordinating 节点只在大查询归并压力明显时才有必要。
索引、分片与副本
- 文档路由:
shard = hash(routing) % number_of_primary_shards,routing默认是_id。 - 主分片数创建后不可改;要改只能 reindex、
_split(拆分)或_shrink(收缩)。 - 副本数可动态调整;副本和主分片不会放在同一节点。
- 写只能走主分片,读可以走主分片或副本。
- 8.x 新建索引默认
number_of_shards=1、number_of_replicas=1;生产按容量显式指定。
分片容量测算
经验值(社区/Elastic 官方指南,不是硬限制):
- 单个分片 10–50GB 比较稳妥;超过 50GB 恢复和 merge 压力明显。
- 每 GB 堆内存对应的分片数尽量 < 20;例如 30GB 堆 → 单节点约 600 个分片上限。
- 分片总数还要考虑副本:副本也占分片名额和恢复带宽。
一个可落地的算法:
主分片数 = ceil(原始数据量 / 目标分片大小)
总副本数 = 主分片数 × (1 + 副本数)
单节点分片数 = 总副本数 / 数据节点数 // 要小于 20 × 堆内存 GB例子:原始数据 1TB,目标分片 30GB,1 副本,3 个数据节点:
主分片 ≈ 1024 / 30 ≈ 35 → 取 36
总副本 = 36 × 2 = 72
单节点 ≈ 72 / 3 = 24 个分片如果节点堆是 32GB,单节点上限约 640 个分片,24 个远低于上限;真正的约束是磁盘和恢复时间,不是分片名额。
用命令验证分片状态
bash
# 每个分片在哪个节点、主还是副本、大小
GET _cat/shards?v&h=index,shard,prirep,state,docs,store,node
# 各节点分片数和磁盘
GET _cat/allocation?v
# 索引级别的主/副本数、文档数、存储大小
GET _cat/indices?v&h=index,pri,rep,docs.count,store.size
# 某个分片为什么没分配
GET _cluster/allocation/explain
{
"index": "orders",
"shard": 0,
"primary": true
}_cluster/allocation/explain 会给出 allocate_explanation 和各个 decider 的结果;磁盘水位、节点离线、副本数大于数据节点数,都会在这里体现。
最小上手
bash
# 单节点验证(8.x)
docker run -d --name es -p 9200:9200 \
-e discovery.type=single-node \
-e xpack.security.enabled=false \
docker.elastic.co/elasticsearch/elasticsearch:8.15.0bash
# 建一个 3 主 1 副的索引
curl -X PUT "localhost:9200/orders" -H 'Content-Type: application/json' -d '
{
"settings": { "number_of_shards": 3, "number_of_replicas": 1 },
"mappings": {
"properties": {
"order_id": { "type": "keyword" },
"status": { "type": "keyword" },
"amount": { "type": "double" },
"created_at": { "type": "date" }
}
}
}'
curl "localhost:9200/_cat/shards/orders?v"
curl "localhost:9200/_cluster/health?pretty"单节点上副本会处于 UNASSIGNED,这是正常的;至少两个数据节点才能分配副本。
生产动作
- 分片数一次定好:按未来 12 个月数据量估,不按当前量估;主分片不可改,扩容靠加节点而不是加主分片。
- 副本:至少 1 个;副本数大于数据节点数会产生未分配分片。
- 专用 master:3 个奇数候选节点,跨机架/可用区;不要让 master 承担大量 data 角色。
- 磁盘水位:默认低水位 85%、高水位 90%、flood stage 95%;到 95% 索引会变成只读。
- 分配排障:
_cluster/allocation/explain是第一步,不要直接reroute强分配。 - 监控:
_cat/shards的UNASSIGNED、_cluster/health的relocating_shards、节点磁盘和堆。
边界
- 主分片数不能改:
_split和_shrink能调整,但都要求索引只读并复制数据,不是在线操作。 - 分片不是越多越好:每个分片都有 Lucene 开销和集群状态开销;过度分片会让 master 和恢复变慢。
- 分片不是越大越好:大分片恢复慢,单分片故障影响面大;50GB 是经验上限,不是硬指标。
- 副本不是备份:副本在同一集群内,误删索引会一起没;备份要用 snapshot。
- 单节点不能验证副本行为:副本会一直未分配,必须至少两个数据节点。
讲清楚
ES 把索引切成主分片,主分片数在创建时固定,文档按
hash(_id) % 主分片数路由;每个主分片可以有副本,负责故障接管和读扩展。主分片不能改,所以分片规划是一次性决策:先按「原始数据量 / 10–50GB」算主分片,再乘副本数,最后检查单节点分片数是否超过「20 × 堆内存 GB」。分片太多会拖慢 master 和恢复,分片太大则恢复慢、故障影响面大。排障先看
_cat/shards和_cluster/allocation/explain,确认是磁盘水位、节点离线还是副本数大于节点数;不要一上来就 reroute。生产上 master 和 data 尽量分离,磁盘到 95% 会只读,副本不能当备份。