真实用scroll分批取出3万条全部文档,验证其绕开默认1万计数上限的精确总数特性,并用search_after完成同样的全量导出任务对比耗时,印证官方现在更推荐用PIT+search_after替代scroll的真实依据。
代码真实索引30000篇文档 POST /scroll_test/_search?scroll=1m {"size":1000,"query":{"match_all":{}},"sort":["_doc"]} → 返回第一批1000条 + 一个scroll_id 真实循环调用,每次用上一次返回的scroll_id继续取下一批: POST /_search/scroll {"scroll":"1m","scroll_id":"<上一次返回的id>"}
total_docs=30000 fetched=30000 batches=30 elapsed_sec=0.122
这组真实耗时验证了scroll的核心设计目标:它真实不是为了"快速翻到某一页",而是为了"一条不漏地把全部匹配结果取完"。每一批返回的1000条文档真实都带着一个指向下一批起点的scroll_id,循环调用直到某一批返回空结果为止,真实能保证把所有30000条文档都取到,这是本系列第50篇验证过的from+size深分页完全做不到的(那种方式超过1万条直接被拒绝)。
代码POST /scroll_test/_search?scroll=1m {"size":1000,"query":{"match_all":{}},"sort":["_doc"]} POST /scroll_test/_search {"size":1,"query":{"match_all":{}}} # 对比不带scroll的普通搜索
scroll首次响应的hits.total:
→ {'value': 30000, 'relation': 'eq'} ← 真实是精确值
不带scroll的普通搜索的hits.total:
→ {'value': 10000, 'relation': 'gte'} ← 真实被截断成"至少1万"这种近似值代码同样3万条文档,真实改用search_after循环取完全部数据(逻辑类似第51篇验证过的游标分页,但这里用于一次性全量导出而非交互式翻页)
search_after: fetched=30000 batches=30 elapsed_sec=0.106 scroll: fetched=30000 batches=30 elapsed_sec=0.122
这组真实耗时对比印证了官方现在的建议:对于"需要完整取出全部匹配结果"这类场景,search_after(配合本系列第52篇验证过的PIT)在性能上真实并不逊色于scroll,而且search_after真实没有scroll那种"必须在服务端持续占用资源直到显式清除或者超时"的负担——scroll context在整个遍历期间真实会持续占用集群资源(锁定底层segment不被合并清理),本文前面验证关闭PIT时提到的那个真实代价对scroll同样适用,而且更重,这也是为什么新版本官方文档把scroll定位成"仅用于极少数真正需要深度遍历、且明确知道数据不会在遍历期间频繁变化的场景"(比如一次性的全量数据迁移或离线分析),日常的分页和导出需求真实更推荐用PIT+search_after替代。
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
_search?scroll 与 _search/scroll API