Elasticsearch(ES)中的分页查询主要有以下四种方式,每种方式适用于不同的场景,且存在不同的性能与功能限制:
一、基础分页:from + size
-
机制:通过
from(起始位置)和size(每页数量)参数实现分页,类似于传统数据库的LIMIT offset, size。GET /your_index/_search { "from": 0, "size": 10, "query": { ... } } -
特点:
- 简单易用:适合小数据量或浅层分页(如查询前100页)。
- 性能瓶颈:深度分页时(如
from > 10000),需在每个分片加载并排序from + size条数据,合并后再截取目标区间,导致内存和CPU消耗剧增,默认限制为max_result_window=10000。 - 不适用场景:大数据量深度分页(如导出全量数据或查询第1000页后的数据)。
二、滚动查询:scroll
-
机制:通过创建索引快照(
snapshot)维护滚动上下文,基于scroll_id逐批获取数据,类似游标遍历。// 初始化 GET /your_index/_search?scroll=1m { "size": 1000, "query": { ... } } // 后续请求 GET /_search/scroll { "scroll_id": "DnF1...", "scroll": "1m" } -
特点:
- 大数据量支持:适合数据导出、批量处理等离线任务,可遍历全量数据。
- 实时性牺牲:基于初始快照查询,不反映后续数据变更。
- 资源消耗:需维护滚动上下文,长期未清理可能导致内存泄漏。
三、实时深度分页:search_after
-
机制:基于上一页最后一条记录的排序值(如时间戳、唯一ID)定位下一页起始点,避免全局排序。
GET /your_index/_search { "size": 10, "query": { ... }, "sort": [{"timestamp": "asc"}, {"_id": "asc"}], "search_after": [1698765432, "abc123"] } -
特点:
-
高性能:无需全局排序,适合深度分页且需实时性的场景(如无限滚动加载)。
-
限制:
- 顺序遍历:仅支持顺序逐页查询,无法跳页。
- 排序字段要求:需至少一个唯一且稳定的排序字段(如时间戳+ID组合)。
-
结合PIT(Point in Time):通过
pit_id固定查询快照,保证分页过程中数据一致性。
-
四、复合聚合分页:composite聚合
-
机制:通过聚合分桶(如按时间区间、分类)实现分页,结合
after_key参数逐批获取结果。GET /your_index/_search { "size": 0, "aggs": { "my_buckets": { "composite": { "size": 100, "sources": [{"field": "category"}] } } } } -
特点:
- 聚合场景专用:适合按维度分页统计(如分页展示分类商品数量)。
- 非通用分页:不适用于文档列表分页,主要用于聚合结果分页。
总结:如何选择分页方式?
| 场景 | 推荐方式 | 注意事项 |
|---|---|---|
| 浅层分页(前100页) | from + size | 避免超过max_result_window限制 |
| 大数据量导出或离线处理 | scroll | 及时清理scroll_id,避免内存泄漏 |
| 实时深度分页(如无限滚动) | search_after | 需唯一排序字段,结合PIT保证一致性 |
| 按维度分页统计 | composite聚合 | 仅适用于聚合结果,非文档列表分页 |