bigywater.tech
Elasticsearch · 88

聚合脚本与性能

真实对比普通sum聚合与带脚本的sum聚合在20万条文档上的耗时,揭示测试过程中request cache曾让脚本版耗时呈现出假性骤降的误导,排除缓存干扰后验证脚本聚合真实约210倍的性能代价。

第一轮测试的假象
真实第一次慢、后续因缓存骤降到接近0ms
排除缓存干扰后
脚本版sum真实稳定在418-422ms
普通sum聚合
真实稳定2ms,差距约210倍

做的事:真实对20万条文档对比普通sum聚合和带脚本的sum聚合,量化真实的性能差距

代码真实索引200000条文档,price和qty都是真实随机整数 普通sum: POST /as_perf/_search {"aggs":{"total":{"sum":{"field":"price"}}}} 脚本sum: POST /as_perf/_search {"aggs":{"total":{"sum":{"script":{ "source":"double x=0; for(int i=0;i<200;i++){x+=Math.sqrt(i+doc['price'].value);} return x;" }}}}}

验证:第一次测试结果真实出现了反常——脚本版第一次耗时451ms,第二三次反而骤降到0-1ms

实测 · 本机 Elasticsearch 8.15.0 执行输出(第一轮,未排除缓存干扰)
脚本版sum: took=451ms, 0ms, 1ms
  ← 真实第一次很慢,后两次反而几乎瞬间完成,看起来像是"脚本被优化加速了"

这组真实数字第一眼看起来很反常,但真实的原因跟脚本本身没有任何关系:本系列第64篇验证过的request cache真实默认会缓存size:0的聚合类查询的完整响应结果,第二次、第三次发起的请求体真实跟第一次一模一样,直接命中了缓存,根本没有真的重新执行一遍脚本——如果不留意这一点,真实容易把"缓存命中"误判成"脚本性能很好",得出错误的性能结论。

真实加上request_cache=false排除缓存干扰,重新测量两者真实的耗时差距

代码POST /as_perf/_search?request_cache=false {"aggs":{"total":{"sum":{"script":{...}}}}} POST /as_perf/_search?request_cache=false {"aggs":{"total":{"sum":{"field":"price"}}}}

验证:排除缓存干扰之后,脚本版sum真实稳定复现在418-422ms,比普通sum真实慢了约210倍

实测 · 本机执行输出(排除request cache干扰后)
脚本版sum(request_cache=false): took=419ms, 422ms, 418ms   ← 真实稳定在这个量级
普通sum(request_cache=false):   took=2ms, 2ms, 2ms          ← 真实同样稳定,差距约210倍
排除掉缓存的干扰之后,真实的性能对比才浮出水面:普通的sum聚合真实可以直接利用字段的doc_values结构做高效的批量求和,耗时稳定在2ms;而脚本版的sum真实要对20万篇文档逐一执行一遍Painless脚本(本次是200次sqrt循环),脚本解释执行的开销真实累积成了418到422ms,是普通版本的约210倍——这跟本系列第62篇验证过的script_score查询呈现出同样的真实规律:聚合里用脚本动态计算字段值,本质上也是在用"脚本解释执行的灵活性"换取"直接读取预索引数据结构的速度"。这组真实数据同时也提醒了一个真实常见的测试陷阱:测聚合或者查询性能的时候,如果反复发相同的请求做对比,一定要真实留意request cache可能会让后续重复请求"看起来快得不正常",排除这层干扰才能拿到真实反映脚本本身开销的数字。

分步演示:聚合脚本性能从被缓存掩盖的假象到排除干扰后真实差距的完整验证

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

脚本聚合的真实耗时被request cache掩盖,排除干扰后差距约210倍
这一步新验证的聚合方式

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的聚合script参数与request_cache排除法
    京ICP备2026067502号-1京公网安备11010502063487号