导读:本期聚焦于周翰文创作的《Elasticsearch from+size分页越翻越慢怎么办?深分页原理与解决方案详解》,敬请观看详情。用from和size做分页时,前几页响应飞快,翻到几千条之后查询延迟却明显上升,甚至直接报错,这是Elasticsearch深分页问题的典型表现。根源在于每个分片都要在内存中构建完整排序队列,from越大,需要拉取和排序的文档数量就越多,代价随偏移量线性增长。本文先拆解from+size的底层执行流程和max_result_window默认一万条限制的来龙去脉,再对比search_after、scroll、PIT游标以及调整索引副本分片等几种常见方案的原理、适用场景与优缺点,帮你针对实时列表页、批量导出、游标遍历等不同业务需求选对方案。

Elasticsearch提供了多种分页方式,其中最直观的就是fromsize的组合:前端传页码,后端翻译成偏移量,一次请求拿到对应页的数据。这种方式在前几页表现非常好,但当你把from调大到几千甚至上万时,查询延迟会明显上升,超过默认的一万条限制还会直接抛出异常。这就是常说的深分页问题。要真正解决这个问题,首先得理解它在底层到底做了什么。

Elasticsearch from+size分页越翻越慢怎么办?深分页原理与解决方案详解

from+size的底层执行流程

一次普通的分页查询到达协调节点后,Elasticsearch并不会只取你需要的那几十条文档。假设索引有5个主分片,查询条件是from=10000, size=10,协调节点会把请求广播到所有分片,每个分片都要返回前from + size条,也就是10010条文档ID和排序值。协调节点拿到5个分片共50050条数据后,在内存中归并排序,再截取第10000到第10010条返回给客户端。

可以看出,实际需要返回的只有10条,但系统内部却处理了五万多条数据,而且这些数据都要占用堆内存。from越大,每个分片需要构建的优先级队列就越大,网络传输和归并排序的开销也线性增长。内存占用与CPU消耗加在一起,使得深分页不仅慢,还可能拖垮整个集群,这也是官方默认设置max_result_window = 10000的原因。这个参数本质上是一道保护墙,防止一次误操作的查询把节点内存打爆。

有些团队遇到报错后第一反应是调大这个参数:

PUT /my_index/_settings
{
  "index": {
    "max_result_window": "50000"
  }
}

这种做法只能临时救急,不解决根本问题。参数调到十万,翻到十万页的代价同样存在,只是报错变成了慢查询,风险反而更隐蔽。正确的思路是根据业务场景换用更合适的分页机制。

search_after:实时列表页的推荐方案

search_after的核心理念是把随机访问变成顺序访问。它不再接受from偏移量,而是要求客户端携带上一页最后一条文档的排序值,服务端从那个位置之后继续取数据。这样每个分片只需要维护一个很小的队列,无论翻到第几页,单次查询的成本都是恒定的。

典型用法如下:

GET /my_index/_search
{
  "size": 10,
  "sort": [
    { "created_at": "desc" },
    { "_id": "asc" }
  ],
  "search_after": [1700000000000, "doc_abc123"],
  "query": {
    "match_all": {}
  }
}

注意排序字段中额外加了_id,这是为了保证排序值的唯一性。如果排序字段有大量重复值,仅靠单一字段定位可能丢失或重复数据,补一个唯一字段作为tie-breaker是标准做法。第一次查询不传search_after,之后每次把上一页最后一条的sort值原样带回即可。

它的局限也很明显:不能跳页。用户只能上一页下一页地顺序翻,无法直接跳到第500页。对于电商商品列表、信息流这类场景,这通常不是问题;但如果产品明确要求页码跳转,就要考虑限制可跳转页数,或者用别的方式兜底,比如只开放前几千条的翻页入口。

scroll与PIT:批量导出的正确姿势

scroll查询适合离线批量拉取全量数据的场景。它第一次查询时在服务端生成一个快照,之后凭scroll_id反复拉取下一批,直到取完为止:

POST /my_index/_search?scroll=2m
{
  "size": 1000,
  "sort": ["_doc"],
  "query": { "match_all": {} }
}

POST /_search/scroll
{
  "scroll": "2m",
  "scroll_id": "DXF1ZXJ5QW5kRmV0Y2gB..."
}

快照机制意味着查询期间的增量数据不可见,这对数据导出、重建索引等任务是优点(保证一致性),但对实时业务页面就是缺点了。同时scroll会长期占用服务端资源,用完必须调用DELETE /_search/scroll显式清理,否则要等超时自动释放。

新版本中官方更推荐用PIT(Point In Time)配合search_after替代scroll。PIT是一种更轻量的快照抽象,先打开一个时间点,再基于它反复执行search_after,切割批次的游标由客户端自己保存,服务端压力更小:

POST /my_index/_pit?keep_alive=2m

GET /_search
{
  "size": 1000,
  "pit": { "id": "46ToAwMDaWR5..." },
  "sort": [{ "created_at": "asc" }],
  "search_after": [1700000000000]
}

scroll在较新的Elasticsearch版本中已被标记为不建议用于实时分页,官方的态度是:实时翻页用search_after,全量遍历用PIT加search_after。

如何为业务选择合适的分页方案

选型的判断依据主要有三个维度:是否需要跳页、数据是否需要实时一致、单次数据量有多大。下表可以作为快速参考:

除了换分页方式,还可以从产品设计上规避深分页:搜索引擎类产品普遍只暴露前几十页结果,因为用户几乎不会看更后面的内容;后台管理系统如果确实需要访问全量数据,更好的做法是提供筛选条件缩小结果集,或者提供导出功能,而不是让用户一页页点到底。

最后回顾一下核心结论:from+size的代价随偏移量线性增长,max_result_window是保护而非障碍;实时顺序翻页交给search_after,全量遍历交给PIT加search_after。理解了每个分片都要为深分页构建大队列这个底层机制,选型时就不会再纠结了。

方案支持跳页实时性典型场景
from+size实时后台管理列表、页数不多的展示页
search_after实时App信息流、商品列表下一页加载
scroll快照旧版本的全量导出
PIT+search_after快照数据迁移、重建索引、批量任务

Elasticsearch深分页from size分页scroll查询修改时间:2026-09-01 10:16:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。