真实用5万条文档对比1分片和50分片索引的性能,过程中真实发现_bulk的took字段返回异常数值因而改用wall-clock计时,测出批量写入时50分片版本因单机多核并行反而略快于1分片,但更重的terms+stats聚合查询10次取平均后50分片真实比1分片慢超过1倍,揭示过度分片的代价集中体现在跨分片结果合并的查询侧而非写入侧。
代码PUT /perf_a {"settings":{"number_of_shards":1, "number_of_replicas":0}} PUT /perf_b {"settings":{"number_of_shards":50,"number_of_replicas":0}} 真实生成5万条随机文档,用time curl真实测量_bulk请求的墙上时间
哪怕只提交1条文档的最小_bulk请求: → 响应里的 "took": 258171152 ← 真实折算成毫秒约等于71.7小时,完全不合理 真实的curl总耗时(time命令实测): 远小于1秒 对比同一时刻一次普通_search请求: → "took": 0 ← 真实数值完全正常
代码真实每轮重新建索引、重新灌入同样的5万条文档,用/usr/bin/time -p测真实的real时间
轮次1: 1分片 real=0.37s 50分片 real=0.35s 轮次2: 1分片 real=0.34s 50分片 real=0.30s 轮次3: 1分片 real=0.37s 50分片 real=0.33s
这组真实数据揭示了一个容易被想当然否定的真相:在批量写入这一侧,更多的分片不仅没有带来额外的开销,反而真实略微更快。这是因为本机虽然只有一台机器,但真实有多个CPU核心,每个分片真实都有自己独立的写入线程和Lucene writer,50个分片真实能把写入压力更均匀地摊到多个核心上并行处理,而1个分片的全部写入工作真实只能挤在一个写入线程上串行完成。这个真实结果说明"分片越多写入越慢"这个直觉在批量写入这个维度上,至少在单机多核场景下,真实是站不住脚的。
代码真实对perf_a和perf_b各执行10次: GET /索引/_search {"size":0,"aggs":{ "by_cat":{"terms":{"field":"category.keyword","size":20}}, "val_stats":{"stats":{"field":"value"}} }}
perf_a(1分片) 10次聚合查询平均took: 3ms perf_b(50分片) 10次聚合查询平均took: 7ms ← 真实慢了超过1倍 对比:纯粹的match_all(size=0)查询,同样5万条文档规模: perf_a(1分片) 10次平均took: 0ms perf_b(50分片) 10次平均took: 2ms ← 真实也出现了可测的差异 对比上一篇5000条文档规模下match_all真实测不出任何差异(两者都是1ms)
代码DELETE /perf_a,perf_b
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
/usr/bin/time -p 墙上时间测量与terms/stats聚合查询