Elasticsearch Search After分页查询所有数据的实现方式

 更新时间:2025年05月16日 10:28:15   作者:二六八  
这篇文章主要介绍了Elasticsearch Search After分页查询所有数据的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

search_after 分页的方式是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。但是需要注意,因为每一页的数据依赖于上一页最后一条数据,所以无法跳页请求。

为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。

上面的请求会为每一个文档返回一个包含sort排序值的数组。这些sort排序值可以被用于 search_after 参数里以便抓取下一页的数据。比如,我们可以使用最后的一个文档的sort排序值,将它传递给 search_after 参数:

注意:当我们使用search_after时,from值必须设置为0或者-1。

search_after缺点是不能够随机跳转分页,只能是一页一页的向后翻,并且需要至少指定一个唯一不重复字段来排序。它与滚动API非常相似,但与它不同,search_after参数是无状态的,它始终针对最新版本的搜索器进行解析。因此,排序顺序可能会在步行期间发生变化,具体取决于索引的更新和删除。

1. search_after 查询

search_after 查询定义与实战案例

search_after 查询本质:使用前一页中的一组排序值来检索匹配的下一页。

前置条件:使用 search_after 要求后续的多个请求返回与第一次查询相同的排序结果序列。也就是说,即便在后续翻页的过程中,可能会有新数据写入等操作,但这些操作不会对原有结果集构成影响。

如何实现呢?

可以创建一个时间点 Point In Time(PIT)保障搜索过程中保留特定事件点的索引状态。

  • Point In Time(PIT)是 Elasticsearch 7.10 版本之后才有的新特性。
  • PIT的本质:存储索引数据状态的轻量级视图。

如下示例能很好的解读 PIT 视图的内涵。

创建 PIT

POST kibana_sample_data_logs/_pit?keep_alive=1m

获取数据量 14074

POST kibana_sample_data_logs/_count

新增一条数据

POST kibana_sample_data_logs/_doc/14075
{
  "test":"just testing"
}

数据总量为 14075

POST kibana_sample_data_logs/_count

查询PIT,数据依然是14074,说明走的是之前时间点的视图的统计

POST /_search
{
  "track_total_hits": true,
  "query": {
    "match_all": {}
  },
  "pit": {
    "id": "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEN3RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA"
  }
}

有了 PIT,search_after 的后续查询都是基于 PIT 视图进行,能有效保障数据的一致性。

search_after 分页查询可以简单概括为如下几个步骤。

Step 1: 创建 PIT

步骤 1:创建 PIT 视图,这是前置条件不能省。

POST kibana_sample_data_logs/_pit?keep_alive=5m

返回结果如下:

{
  "id" : "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEg5RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA"
}

keep_alive=5m,类似scroll的参数,代表视图保留时间是 5 分钟。

超过 5 分钟执行会报错如下:

{
  "type": "search_context_missing_exception",
  "reason": "No search context found for id [91600]"
}

Step 2: 创建基础查询

步骤 2:创建基础查询语句,这里要设置翻页的条件。

GET /_search
{
  "size":10,
  "query": {
    "match" : {
      "host" : "elastic"
    }
  },
  "pit": {
     "id":  "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEg5RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA", 
     "keep_alive": "1m"
  },
  "sort": [ 
    {"response.keyword": "asc"}
  ]
}

设置了PIT,检索时候就不需要再指定索引。

id 是基于步骤1 返回的 id 值。

排序 sort 指的是:按照哪个关键字排序。

在每个返回文档的最后,会有两个结果值,如下所示:

{
  "sort": [
    "200",
    4
  ]
}

其中,“200”就是我们指定的排序方式:基于 {“response.keyword”: “asc”} 升序排列。

而 4 代表什么含义呢?

4 代表——隐含的排序值,是基于_shard_doc 的升序排序方式。

官方文档把这种隐含的字段叫做:tiebreaker (决胜字段),tiebreaker 等价于_shard_doc。

tiebreaker 本质含义:每个文档的唯一值,确保分页不会丢失或者分页结果数据出现重复(相同页重复或跨页重复)。

step 3 : 开始翻页

步骤3:实现后续翻页。

GET /_search
{
  "size": 10,
  "query": {
    "match" : {
      "host" : "elastic"
    }
  },
  "pit": {
     "id":  "48myAwEXa2liYW5hX3NhbXBsZV9kYXRhX2xvZ3MWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAWdG1TOWFMTF9UdTZHdVZDYmhoWUljZwAAAAAAAAEg5RZGOFJCMGVrZVNndTk3U1I0SG81V3R3AAEWM2hGWXpxLXFSSGlfSmZIaXJWN0dxUQAA", 
     "keep_alive": "1m"
  },
  "sort": [
    {"response.keyword": "asc"}
  ],
  "search_after": [                                
    "200",
    4
  ]
}

后续翻页都需要借助 search_after 指定前一页的最后一个文档的 sort 字段值。

如下代码所示:

{
  "search_after": [
    "200",
    4
  ]
}

显然,search_after 查询仅支持向后翻页。

search_after 查询优缺点及适用场景

search_after 优点

  • 不严格受制于 max_result_window,可以无限制往后翻页。
  • ps:不严格含义:单次请求值不能超过 max_result_window;但总翻页结果集可以超过。

search_after 缺点

  • 只支持向后翻页,不支持随机翻页。

search_after 适用场景

  • 类似:今日头条分页搜索 https://m.toutiao.com/search
  • 不支持随机翻页,更适合手机端应用的场景。

2. Scroll 遍历查询

Scroll 遍历查询定义与实战案例

相比于 From + size 和 search_after 返回一页数据,Scroll API 可用于从单个搜索请求中检索大量结果(甚至所有结果),其方式与传统数据库中游标(cursor)类似。

如果把 From + size 和 search_after 两种请求看做近实时的请求处理方式,那么 scroll 滚动遍历查询显然是非实时的。数据量大的时候,响应时间可能会比较长。

scroll 核心执行步骤如下:

步骤 1:指定检索语句同时设置 scroll 上下文保留时间。

实际上,scroll 已默认包含了 search_after 的PIT 的视图或快照功能。

从 Scroll 请求返回的结果反映了发出初始搜索请求时索引的状态,类似在那一个时刻做了快照。随后对文档的更改(写入、更新或删除)只会影响以后的搜索请求。

POST kibana_sample_data_logs/_search?scroll=3m
{
  "size": 100,
  "query": {
    "match": {
      "host": "elastic"
    }
  }
}

步骤 2:向后翻页继续获取数据,直到没有要返回的结果为止。

POST _search/scroll                                   
{
  "scroll" : "3m",
  "scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFkY4UkIwZWtlU2d1OTdTUjRIbzVXdHcAAAAAAAGmkBZ0bVM5YUxMX1R1Nkd1VkNiaGhZSWNn" 
}

scroll_id 值是步骤 1 返回的结果值。

Scroll 遍历查询优缺点及适用场景

scroll 查询优点

  • 支持全量遍历。
  • ps:单次遍历的 size 值也不能超过 max_result_window 大小。

scroll 查询缺点

  • 响应时间非实时。
  • 保留上下文需要足够的堆内存空间。

scroll 查询适用场景

  • 全量或数据量很大时遍历结果数据,而非分页查询。
  • 官方文档强调:不再建议使用scroll API进行深度分页。如果要分页检索超过 Top 10,000+ 结果时,推荐使用:PIT + search_after。

总结

  • From+ size:需要随机跳转不同分页(类似主流搜索引擎)、Top 10000 条数据之内分页显示场景。
  • search_after:仅需要向后翻页的场景及超过Top 10000 数据需要分页场景。
  • Scroll:需要遍历全量数据场景 。
  • max_result_window:调大治标不治本,不建议调过大。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详述 DB2 分页查询及 Java 实现的示例

    详述 DB2 分页查询及 Java 实现的示例

    本篇文章主要介绍了详述 DB2 分页查询及 Java 实现的示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • Java使用反射调用方法示例

    Java使用反射调用方法示例

    这篇文章主要介绍了Java使用反射调用方法,结合实例形式分析了java使用反射调用对象方法的相关操作技巧,需要的朋友可以参考下
    2019-07-07
  • MyBatis-Plus简介和快速入门教程

    MyBatis-Plus简介和快速入门教程

    MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做修改,为简化开发、提高效率而生,本文给大家介绍MyBatis-Plus简介和快速入门教程,需要的朋友参考下吧
    2021-09-09
  • 扫二维码自动跳转【java】详解

    扫二维码自动跳转【java】详解

    这篇文章主要介绍了java扫二维码自动跳转,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • JAVA 枚举相关知识汇总

    JAVA 枚举相关知识汇总

    这篇文章主要介绍了JAVA 枚举相关知识,文中讲解的非常详细,代码帮助大家更好的参考和学习,感兴趣的朋友可以了解下
    2020-06-06
  • SpringMVC HttpMessageConverter报文信息转换器

    SpringMVC HttpMessageConverter报文信息转换器

    ​​HttpMessageConverter​​​,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文。​​​HttpMessageConverter​​​提供了两个注解和两个类型:​​@RequestBody,@ResponseBody​​​,​​RequestEntity,ResponseEntity​​
    2023-01-01
  • SpringBoot整合Swagger页面禁止访问swagger-ui.html方式

    SpringBoot整合Swagger页面禁止访问swagger-ui.html方式

    本文介绍了如何在SpringBoot项目中通过配置SpringSecurity和创建拦截器来禁止访问SwaggerUI页面,此外,还提供了禁用SwaggerUI和Swagger资源的配置方法,以确保这些端点和页面对外部用户不可见或无法访问
    2025-02-02
  • 详解SpringBoot 应用如何提高服务吞吐量

    详解SpringBoot 应用如何提高服务吞吐量

    这篇文章主要介绍了Spring Boot 应用如何提高服务吞吐量,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • SpringBoot发送html邮箱验证码功能

    SpringBoot发送html邮箱验证码功能

    这篇文章主要介绍了SpringBoot发送html邮箱验证码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • SpringMVC框架实现Handler处理器的三种写法

    SpringMVC框架实现Handler处理器的三种写法

    这篇文章主要介绍了SpringMVC框架实现Handler处理器的三种写法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02

最新评论