Elasticsearch Search After分页查询所有数据

Search After分页查询所有数据

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

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

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

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

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
不支持随机翻页,更适合手机端应用的场景。

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。

3、小结

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

参考

  • Elasticsearch 深入理解search After 处理深度分页问题
  • Elasticsearch:使用search after实现深度分页
  • 干货 | 全方位深度解读 Elasticsearch 分页查询

你可能感兴趣的:(Elasticsearch,elasticsearch,java,大数据)