Elasticsearch(ES)分页
Elasticsearch(简称 ES)本身不适合传统意义上的“深分页”,但提供了多种分页方式,每种适用不同场景。我们来详细讲解:
一、基本分页(from + size)
最常用的分页方式,类似 SQL 的 LIMIT offset, size
。
查询语法示例:
GET /products/_search
{"query": {"match_all": {}},"from": 20,"size": 10
}
表示从第 21 条记录开始,取 10 条(第 3 页,每页 10 条)
优点:
- 简单直观,适合前几页浏览。
缺点:
- 深分页性能差:from 越大,ES 处理速度越慢,浪费内存和 CPU。
- 默认最大
from + size
为 10000(可修改index.max_result_window
)
二、Scroll 分页(适合大批量导出)
Scroll 是一种游标查询,用于大量数据批量处理,如导出所有数据。
查询示例:
POST /products/_search?scroll=1m
{"size": 100,"query": {"match_all": {}}
}
响应返回:
{"_scroll_id": "...","hits": {"hits": [ ... ]}
}
继续获取下一页:
POST /_search/scroll
{"scroll": "1m","scroll_id": "..."
}
优点:
- 适合全量导出、日志批处理等任务。
- 内部避免重新排序,性能好。
缺点:
- 不是实时的,不能跳页。
- scroll 占内存,需要及时清除。
三、Search After(推荐用于深分页)
官方推荐用于实时系统中分页跳转到深页,无需使用 from
。
使用前提:
- 必须排序
- 每页记录中携带上页最后一条的排序字段值
示例:
第一页请求:
POST /products/_search
{"size": 10,"sort": [{ "price": "asc" }, { "_id": "asc" }]
}
返回结果中,拿到最后一条记录的排序值:
"sort": [100.0, "abc123"]
第二页请求使用 search_after
:
POST /products/_search
{"size": 10,"sort": [{ "price": "asc" }, { "_id": "asc" }],"search_after": [100.0, "abc123"]
}
优点:
- 性能优于 from + size 的深分页
- 可实时查询,稳定
缺点:
- 不支持跳页(只能“下翻页”)
- 前端不能直接点跳转页码(需记住前页的 sort 值)
四、Pit(Point in Time)+ Search After(ES 7.10+)
在使用 search_after
时,为避免分页期间数据变动带来的不一致问题,可以使用 PIT。
创建 PIT:
POST /products/_pit?keep_alive=1m
返回:
{"pit_id": "xxx"
}
查询:
POST /products/_search
{"size": 10,"pit": {"id": "xxx","keep_alive": "1m"},"sort": [{ "price": "asc" }, { "_shard_doc": "asc" }],"search_after": [...]
}
各分页方式对比
方式 | 跳页支持 | 深分页性能 | 实时性 | 适合场景 |
---|---|---|---|---|
from + size | ✅ | ❌ | ✅ | 首页、少量分页 |
scroll | ❌ | ✅ | ❌ | 导出/批处理 |
search_after | ❌ | ✅✅ | ✅ | 深分页、翻页 |
pit + search_after | ❌ | ✅✅✅ | ✅ | 实时且一致性强 |
实践建议
分页需求类型 | 建议用法 |
---|---|
普通分页 | from + size |
超过1万条数据导出 | scroll |
深分页、实时查询 | search_after + sort |
高一致性深分页 | pit + search_after |