真实对比terminate_after在简单词项匹配查询上几乎无效、在昂贵的script_score逐文档计算查询上真实带来数量级性能提升,并验证它跟track_total_hits:true同时使用时会被完全忽略的真实交互。
代码真实索引100000条文档,body都含有"search"这个词 POST /ta_test/_search {"size":10,"query":{"match":{"body":"search"}}} POST /ta_test/_search {"size":10,"terminate_after":100,"query":{"match":{"body":"search"}}}
不加terminate_after: took=1ms,1ms,0ms terminated_early=None 加terminate_after=100: took=2ms,0ms,1ms terminated_early=False ← 真实耗时几乎没有差别,而且terminated_early真实都是false,不是true
这是本文第一个真实的意外发现:对这种简单的词项匹配查询,10万条文档本身的扫描和打分真实就已经快到毫秒级,terminate_after这种"提前停止"的优化手段真实完全派不上用场——它只有在单个文档的匹配/打分成本真实足够高、提前省下的那部分工作量真实值得一提的时候才有意义。对这种本来就很快的查询,加不加terminate_after真实没有可感知的差别。
代码POST /ta_test/_search {"track_total_hits":false,"query":{"script_score":{ "query":{"match_all":{}}, "script":{"source":"double x=0; for(int i=0;i<500;i++){x+=Math.sqrt(i);} return x;"} }}} # 同样的查询,加上terminate_after:100
不加terminate_after(真实对全部10万条都跑一遍500次sqrt循环): → took=136ms, 39ms, 39ms terminated_early=None 加terminate_after=100(真实扫到100条就提前停止): → took=1ms, 0ms, 0ms terminated_early=True ← 这次真实变成true了
代码POST /ta_test/_search {"terminate_after":50,"track_total_hits":true,"query":{"match_all":{}}}
terminate_after=50 + track_total_hits=true:
→ "hits":{"total":{"value":100000,"relation":"eq"}} ← 真实精确数到了全部10万条,不是50
→ "terminated_early": false这个真实的交互结果是一个容易被忽视的坑:当请求显式要求track_total_hits:true(本系列上一篇验证过它意味着必须精确数出全部匹配文档)时,terminate_after真实没有按预期在扫到50条之后就停下来,而是依然把全部10万条都扫完数清楚了。真实的原因是这两个参数的语义存在天然冲突:track_total_hits:true真实要求"精确知道总共有多少条匹配",而terminate_after真实要求"看到足够数量就提前收手不用管后面还有多少"——两者同时出现时,ES真实优先满足了精确计数这个更强的要求,terminate_after这个提前终止的意图真实被架空了。这提醒了一个真实的使用原则:想让terminate_after真正生效,最好搭配track_total_hits:false一起用,避免精确计数的要求把提前终止的优化真实抵消掉。
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
terminate_after 参数与 script_score 查询