3. 分片与副本机制深入
Elasticsearch 把一个索引在逻辑上切成若干主分片(primary shard),每个主分片是一个独立的 Lucene 索引,可以单独承载写入与查询。每个主分片还可以有若干副本分片(replica shard),副本是主分片的完整拷贝,平时不接收写入,只提供读能力,并在主分片所在节点故障时被提升为新的主分片。理解“主分片决定容量上限、副本决定可用性与读能力”这条主线,是后续容量规划与高可用设计的基础。
主分片与副本分片:职责分工
主分片数量在索引创建时确定,之后不能直接修改(只能通过 _reindex 或 _shrink/_split 重建)。副本数量可以随时通过 _settings 调整。一个索引的总分片数 = 主分片数 × (1 + 副本数)。例如 3 个主分片、1 个副本,则总共有 6 个分片,分布在集群节点上。
写入时,请求先到达协调节点,由它根据路由算法定位目标主分片,主分片写入成功后,再并行把数据转发给该主分片的所有副本分片,副本写入成功后才向客户端返回成功。因此副本越多,写入链路越长,写入吞吐会下降,但读并发能力和容错能力提升。
路由算法:文档如何找到分片
Elasticsearch 使用如下公式决定一个文档落在哪个主分片:
shard = hash(routing) % number_of_primary_shards
默认 routing 是文档的 _id。这个公式解释了为什么主分片数不能随意修改:一旦主分片数变化,同一个文档 ID 计算出的分片位置就会改变,已有数据将无法被正确检索。因此主分片数必须在建索引时规划好。
路由也带来一个实用技巧:如果希望某些文档(如同一用户的订单)落在同一分片,可以显式指定 routing 值,让它们共享同一路由键,从而在查询时只命中一个分片,减少扇出。
分片分配策略:分片如何落到节点上
分片分配由主节点(master)负责决策,核心约束包括:
- 同一分片的主副本不能在同一节点:否则节点故障会同时丢失主副本,失去容错意义。
- 尽量均衡:让各节点的分片数量、磁盘占用尽量接近,避免热点节点。
- 感知机架/可用区:通过
cluster.routing.allocation.awareness.attributes配置,让主副本分布到不同机架或可用区。 - 磁盘水位控制:低水位(默认 85%)不再分配新分片,高水位(默认 90%)尝试迁出分片,洪水水位(默认 95%)对索引写阻塞。
当节点加入或离开时,主节点会触发分片再平衡(rebalance),把分片迁移到更合适的位置。再平衡会消耗网络与磁盘 IO,因此在大规模集群中通常需要限制并发迁移数(cluster.routing.allocation.cluster_concurrent_rebalance)。
分片数量对查询性能的影响
查询时,协调节点会把请求广播到索引的所有分片(主或副本均可),每个分片在本地执行查询并返回结果,协调节点再合并、排序、取全局 Top N。因此分片数越多,扇出越大,协调节点合并开销越高,查询延迟通常上升。但分片数太少,单个分片过大,又会导致单分片查询变慢、无法充分利用多节点并行能力。
经验上,单个分片大小控制在 10GB~50GB 较为常见,搜索类场景可偏小(10~30GB),日志类场景可偏大(30~50GB)。分片数应大致等于“数据总量 ÷ 单分片目标大小”,并向上取整到便于分配的整数。
分片数量对写入吞吐的影响
写入吞吐与分片数并非线性正相关。分片数增加,可以并行写入更多分片,提升整体吞吐;但每个分片本身是一个 Lucene 索引,写入时需要维护 segment、refresh、translog,分片过多会带来额外的合并与元数据开销。此外,副本数增加会成倍放大写入链路,因为每次写入都要同步到所有副本。
一个实用的判断方法是:观察 _nodes/stats 中的 indexing.index_time_in_millis 与 refresh 指标,如果写入延迟主要消耗在 refresh 与 merge,说明分片过小、数量过多;如果写入集中在少数节点,说明分片分布不均。
分片数量对存储容量的影响
每个分片都有固定的元数据与文件句柄开销,分片数越多,集群状态(cluster state)越大,主节点维护成本越高。副本分片会占用与主分片相同的存储空间,因此总存储 = 主分片数据量 × (1 + 副本数)。规划容量时必须把副本、segment 合并期间的临时空间、以及预留的磁盘水位(建议不超过 85%)一并考虑。
分片规划检查清单
| 检查项 | 建议值 / 判断依据 |
|---|---|
| 单分片大小 | 搜索 10~30GB,日志 30~50GB |
| 主分片数 | 数据总量 ÷ 单分片目标大小,向上取整 |
| 副本数 | 生产环境至少 1,关键业务可 2 |
| 每节点分片数 | 建议不超过 20~25 个/GB 堆内存 |
| 磁盘水位 | 低 85%、高 90%、洪水 95% |
| 路由键 | 需要同分片查询时显式指定 routing |
分片规划没有一劳永逸的答案,它需要在查询延迟、写入吞吐、存储成本与容错能力之间做权衡。实践中建议先用小规模数据做基准测试,观察不同分片数下的查询 P95 延迟与写入速率,再结合容量水位确定最终方案。