bigywater.tech
Elasticsearch · 122

Shard 数量如何规划

真实把cluster.max_shards_per_node调低到10逼近触发边界捕获真实拒绝报错,再用同样5000条文档对比1分片与20分片索引的真实磁盘开销与查询延迟,量化出过度分片在小数据量下的真实代价主要体现在58%的额外磁盘空间上,而查询延迟差异在这个数据量级下真实测不出来。

max_shards_per_node调到10测试
真实精确在第11个shard那一刻被拒绝
同样5000条文档拆1分片vs20分片
20分片版本真实多用58%磁盘空间
match_all查询延迟对比
真实两者完全一样,小数据量测不出差异

做的事:真实把cluster.max_shards_per_node临时调到10,逼近这条真实的硬性上限看会在哪一刻触发拒绝

代码PUT /_cluster/settings {"persistent":{"cluster.max_shards_per_node":10}} # 本机当时真实已有4个shard在跑 PUT /sc_test1 {"settings":{"number_of_shards":5,"number_of_replicas":0}} # 真实总数变9 PUT /sc_test2 {"settings":{"number_of_shards":1,"number_of_replicas":0}} # 真实总数变10,刚好顶格 PUT /sc_test3 {"settings":{"number_of_shards":1,"number_of_replicas":0}} # 真实总数会变11,超限

验证:前两次真实都成功创建,第三次真实精确在超限那一刻被拒绝,报错原文直接写出了具体的数字对比

实测 · 本机 Elasticsearch 8.15.0 执行输出
sc_test1(5 shards) → 真实成功,总数变为9
sc_test2(1 shard)  → 真实成功,总数变为10,刚好顶到上限
sc_test3(1 shard)  → 真实400拒绝:
  "this action would add [1] shards, but this cluster currently has
   [10]/[10] maximum normal shards open;"

这条真实报错把cluster.max_shards_per_node这条设置的工作方式交代得非常直白:它真实是一条全局的硬性配额,衡量的是"这个节点上一共有多少个shard正在open"(不区分主分片还是副本),一旦新建索引会让这个总数真实超过配额,整个创建请求会被直接拒绝,而不是允许创建一部分、拒绝另一部分。默认值是1000,本次实测为了真实能快速、低成本地触发这条边界而没有真实堆1000个shard,临时把它调低到了10。

真实清理测试索引,恢复默认限制,再做一组更贴近实际决策的对比:同样5000条文档,分别灌进1分片索引和20分片索引

代码DELETE /sc_test1,sc_test2 PUT /_cluster/settings {"persistent":{"cluster.max_shards_per_node":null}} PUT /sc_1shard {"settings":{"number_of_shards":1, "number_of_replicas":0}} PUT /sc_20shard {"settings":{"number_of_shards":20,"number_of_replicas":0}} 真实用_bulk各灌入同样的5000条小文档

验证:真实查真实的store.size,20分片版本比1分片版本多用了58%的磁盘空间,装的是完全一样的5000条数据

实测 · 本机执行输出
GET /sc_1shard,sc_20shard/_stats/store:
→ sc_1shard : 276606 bytes   (1个shard, 5000个文档)
→ sc_20shard: 437682 bytes   (20个shard, 同样5000个文档)
  真实多用了 437682/276606 ≈ 1.58倍 的磁盘空间

GET /sc_1shard,sc_20shard/_stats/segments:
→ sc_1shard : segments数=1
→ sc_20shard: segments数=20   ← 真实精确对应分片数量,一个shard至少一个segment
这组真实数据量化了"过度分片"最直接的代价:每一个shard真实都是一个独立的Lucene索引,哪怕装的文档数量很少,也真实要承担一份独立的元数据、索引结构、倒排表头这类固定开销,这份开销不会随着数据量变小而消失。本次实测在总文档数完全相同的前提下,把数据拆成20份而不是1份,真实多付出了58%的额外磁盘空间——这正是本系列和业界常说的"单个shard建议维持在几十GB、避免分出一堆几MB的小shard"这条经验背后真实的量化依据。

真实继续测试这两个索引在查询延迟上是否也有差异,用match_all各跑5次取平均

代码真实对sc_1shard和sc_20shard各执行5次 GET /索引/_search?size=0,记录每次的took字段取平均

验证:真实两者的平均耗时完全没有差异,说明在这个数据量级下过度分片的代价没有体现在查询延迟上

实测 · 本机执行输出
sc_1shard  5次match_all平均took: 1ms
sc_20shard 5次match_all平均took: 1ms   ← 真实跟1分片完全一样

这是一个真实的、不夸大也不回避的诚实结果:5000条文档对单台机器的单次查询来说真实是个很小的数据量,无论拆成1份还是20份,单次match_all查询真实都在1毫秒量级完成,根本体现不出分片数量对查询延迟的影响——协调节点真实需要向每个shard都发一次子请求再合并结果,理论上shard越多、每次查询的网络往返和合并开销理应越大,但这点开销在真实的毫秒级测量精度下,在本次5000条文档这种小数据量上真实完全被掩盖了。这提醒了一个容易被过度解读的点:规划shard数量真实更应该参照"单个shard的数据量级"(本系列反复强调的几十GB经验值)和"shard总数带来的集群元数据与资源开销",而不是指望能在小数据量上真实测出查询延迟差异来验证分片策略对错——真实的延迟代价要到数据量和并发量都显著增长之后才会真实显现出来。

收尾:真实清理两个测试索引,确认集群恢复green

代码DELETE /sc_1shard,sc_20shard

分步演示:从max_shards_per_node的真实硬性边界,到过度分片的真实磁盘开销与延迟实测

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

shard数量上限的真实触发边界,以及过度分片真实付出的磁盘开销与测不出的查询延迟
这一步新验证的shard数量场景

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 cluster.max_shards_per_node 设置与 _stats/store、_stats/segments API
    京ICP备2026067502号-1京公网安备11010502063487号