Python 操作 Elasticsearch 实现数据翻页的三种方式详解
本文详解 Python 中使用 Elasticsearch 进行数据翻页的三种方式。针对大数据量场景,传统 from+size 浅分页存在性能瓶颈,推荐使用 scroll 滚动查询或 search_after 基于游标的深分页。文章对比了各方案的原理、适用场景及代码实现,帮助开发者解决 ES 集群内存压力与数据一致性难题。

在使用 Elasticsearch 作为搜索引擎提取数据时,若数据规模庞大,采用传统的 from + size 方式无法获取全部结果(默认最大记录数限制为 10000)。这是因为随着页码增加,查询会消耗大量内存,进而导致 ES 集群运行不稳定。为此,Elasticsearch 衍生出了 scroll 和 search_after 等更高效的翻页机制。
from + size 浅分页原理与局限
一、from + size 浅分页
所谓的“浅”分页,即简单的分页逻辑。其原理是:先查询前 20 条数据,然后截断前 10 条,仅返回第 11 至 20 条数据。这种做法实际上浪费了前 10 条数据的查询资源。
GET test/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"age": 28
}
}
]
}
},
"size": 10,
"from": 20,
"sort": [
{
"timestamp": {
"order": "desc"
},
"_id": {
"order": "desc"
}
}
]
}
参数 from 定义了目标数据的偏移量,size 定义了当前返回的数据条数。默认情况下,from 为 0,size 为 10,即所有查询默认仅返回前 10 条数据。
有必要了解 from/size 的工作原理:由于 ES 基于分片机制,假设存在 5 个分片,当设置 from=100,size=10 时,ES 会根据排序规则从每个分片中各取回 100 条数据,汇总成 500 条数据后,再从中筛选出最后的 10 条数据返回。
经过测试,越靠后的分页执行效率越低。总体而言,随着 from 值的增加,查询耗时也会随之增加,且数据量越大,这一现象越为明显。
scroll 滚动查询机制
二、scroll 深分页
当 from+size 查询的数据量在 10000 至 50000 条(约 1000 到 5000 页)以内时,该方法尚可接受;但若数据量过大,则会出现深分页问题。为解决上述问题,Elasticsearch 提出了 scroll 滚动查询方式。scroll 类似于 SQL 中的 cursor,每次仅能获取一页内容,并返回一个 scroll_id。通过该 scroll_id 可不断获取下一页内容,因此 scroll 不适用于需要跳页的场景。
# -*- coding: utf-8 -*-
# @Time :
# @Author :
from elasticsearch import Elasticsearch
es = Elasticsearch(hosts="ip:9200", timeout=20, max_retries=10, retry_on_timeout=True)
# Elasticsearch 需要保持搜索的上下文环境多久 游标查询过期时间为10分钟(10m)
page = es.search(
index="source_keyword_message", doc_type="source_keyword_message",
scroll='10m',
size=100,
body={
"query": {"match_all": {}},
}
)
# 游标用于输出es查询出的所有结果
sid = page['_scroll_id']
# es查询出的结果总量
scroll_size = page['hits']['total']
# es查询出的结果第一页
datas = page.get('hits').get('hits')
while (scroll_size > 0):
page = es.scroll(scroll_id=sid, scroll='5m')
sid = page['_scroll_id']
scroll_size = len(page['hits']['hits'])
datas = page.get('hits').get('hits')
- 设置 scroll=5m 表示指定 scroll_id 的保留时间为 5 分钟。
- 使用 scroll 时必须将 from 设置为 0(默认值即为 0)。
- size 参数决定后续每次调用 _search 接口返回的数据数量。
search_after 基于游标的深分页
三、search_after 深分页
官方建议 scroll 方式不应用于实时请求(通常用于数据导出场景),因为每个 scroll_id 不仅占用大量资源,还会生成历史快照,且数据变更不会反映在该快照上。
search_after 分页方式依据上一页的最后一条数据来确定下一页的位置。在分页请求过程中,若索引数据发生增删改查,这些变更会实时反映到游标上。但需注意,由于每一页数据依赖于上一页最后一条数据,因此无法进行跳页请求。
为了定位每一页的最后一条数据,每个文档必须拥有全局唯一值。官方推荐使用 _uid 作为全局唯一标识,实际上使用业务层的 id 亦可。
GET test/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"age": 28
}
}
]
}
},
"size": 20,
"from": 0,
"sort": [
{
"timestamp": {
"order": "desc"
},
"_id": {
"order": "desc"
}
}
]
}
- 使用 search_after 时必须设置 from=0。
- 此处示例使用 timestamp 和 _id 作为唯一值进行排序。
- 在返回的最后一条数据中,提取 sort 属性的值,并将其传入 search_after 参数中。
利用 sort 返回的值来搜索下一页数据:
GET test/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"age": 28
}
}
]
}
},
"size": 10,
"from": 0,
"search_after": [
1541495312521,
"d0xH6GYBBtbwbQSP0j1A"
],
"sort": [
{
"timestamp": {
"order": "desc"
},
"_id": {
"order": "desc"
}
}
]
}
至此,关于 Python 使用 Elasticsearch 实现翻页的三种方式介绍完毕。如需了解更多 Python 与 Elasticsearch 翻页相关内容,请搜索本站往期文章或继续浏览后续文章,感谢大家的支持!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













