在Elasticsearch中,常用的分页方案有from + size
、search_after
和scroll
三种,适用于不同场景。from + size
基于偏移量分页,是全局排序后的切片查询,适用于小数据量、浅分页场景,但深度分页性能差,且有默认上限限制。search_after
基于游标分页,实现无偏移量的前向分页,无深度分页限制,性能稳定,适合大数据量、实时分页场景,是ES推荐方案,但仅支持“下一页”操作,且排序字段需组合唯一。scroll
基于快照批量分页,适合非实时、大数据量导出场景,能避免重复计算,但实时性差,长时间持有会占用集群内存,ES 7.x版本开始标记其为弃用。三种方案各有优劣,建议优先选择search_after
,谨慎使用from + size
,仅用scroll
做离线导出。
一、from + size
查询:基于偏移量的分页
核心原理
通过 from
(起始偏移量)和 size
(每页数量)参数控制返回结果的范围,本质是全局排序后的切片查询。
例如:from=10, size=10
表示跳过前10条数据,返回第11-20条数据。
执行流程
- 协调节点向所有分片发送查询请求;
- 每个分片根据查询条件(如排序)返回前
from + size
条数据; - 协调节点将所有分片的结果合并,重新排序后取
[from : from+size]
的数据返回。
关键限制与缺陷
- 深度分页性能差:当
from
很大(如from=10000
)时,每个分片需要返回10000+size
条数据,网络传输和内存开销随from
增大呈指数级增长。 - 默认上限限制:ES 为避免深度分页崩溃,默认限制
from + size ≤ 10000
(由index.max_result_window
控制),超过会直接报错。 - 排序依赖:若查询未显式排序(默认按
_score
排序),深度分页可能因评分计算的不稳定性导致结果偏移。
适用场景
仅适用于小数据量、浅分页(如 from < 1000
)的场景,例如前端常规的“下一页”操作(每页10条,最多翻1000页)。
二、search_after
查询:基于游标(Cursor)的分页
核心原理
通过记录前一页最后一条数据的排序值作为游标(search_after
参数),实现无偏移量的前向分页。每次查询仅需从游标位置往后取数据,避免了 from + size
的全局排序开销。
执行流程
- 初始查询:必须显式指定排序字段,并且必须是值唯一的字段,否则会出现数据统计丢失的情况,并返回排序后的第一页数据;
- 获取游标:记录当前页最后一条数据的排序值(如
{"timestamp": 1620000000, "id": "doc123"}
); - 后续查询:将游标作为
search_after
参数,请求下一页数据。
关键特性
- 无深度分页限制:无需加载
from + size
条数据,每次查询仅需获取size
条后续数据,性能稳定; - 前向分页:仅支持“下一页”操作,无法跳转到任意页(需结合业务层缓存游标);
- 排序字段唯一性:排序字段需组合唯一(如
[timestamp, id]
),避免因排序值重复导致游标失效。
适用场景
适合大数据量、实时分页场景(如日志检索、实时报表),是 ES 推荐的分页方案。
三、scroll
查询:基于快照的批量分页
核心原理
通过 scroll
参数生成一个“搜索上下文快照”(类似数据库的游标),后续请求通过 scroll_id
从快照中获取数据。快照在集群内存中临时保存(通过 scroll=1m
指定超时时间),避免重复计算。
执行流程
- 初始化快照:首次查询携带
scroll=1m
参数,ES 返回第一页数据和scroll_id
; - 滚动获取数据:后续请求使用
scroll_id
和scroll=1m
参数,ES 从快照中返回下一页数据; - 释放资源:数据读取完毕后,需显式调用
clear-scroll
API 释放快照资源。
关键特性
- 离线式分页:快照生成后,后续数据变更(增删改)不会影响结果(适合需要一致性的全量导出);
- 高性能遍历:避免重复排序和计算,适合百万级以上数据的批量拉取;
- 实时性差:==快照是静态的,无法反映查询后的实时数据变化; ==
- 资源占用:==长时间持有
scroll_id
会占用集群内存,需谨慎设置超时时间。 ==
适用场景
仅适用于非实时、大数据量导出场景(如数据迁移、离线报表生成)。ES 7.x 版本开始标记 scroll
为弃用(_search/scroll
),推荐使用 search_after
替代实时场景。
四、 max_result_window 限制
from + size ≤ index.max_result_window,即总偏移量与页大小之和不能超过该阈值,超过则直接报错(Result window is too large);
对于scroll和search after,单次请求的size ≤ index.max_result_window。
五、三种分页方案对比总结
特性 | from + size | search_after | scroll |
---|---|---|---|
核心逻辑 | 偏移量切片 | 游标前向遍历 | 静态快照批量拉取 |
深度分页支持 | 不支持(from ≤ 10000 ) | 无限制 | 无限制 |
实时性 | 高(每次查询最新数据) | 高(基于实时排序) | 低(基于快照) |
内存/网络开销 | 高(随 from 增大) | 低(仅获取后续数据) | 中(快照占用内存) |
分页方向 | 任意页(前后跳转) | 仅前向(下一页) | 仅前向(下一页) |
典型场景 | 小数据量浅分页 | 大数据量实时分页 | 全量数据离线导出 |
总结建议
- 优先选择
search_after
:适合绝大多数实时分页场景,兼顾性能与灵活性; - 谨慎使用
from + size
:仅用于小数据量且分页深度较浅的场景; - 仅用
scroll
做离线导出:避免长期持有快照,及时释放scroll_id
。