ElasticSearch三种分页查询方式

 2022-08-03    0 条评论    12460 浏览

es

ES三种分页查询调研

From size浅分页

功能说明

  • ES提供from size的查询方式,是ES针对分页查询提供的一种浅查询的功能。
  • 这种查询方式使用起来和传统数据库的分页查询类似。
  • 但实际对于ES来说,就是将所有数据都查询出来,然后值返回给我们需要的条数。

查询语句

{
    "from" : 10, "size" : 10,
    "query" : {
        "term" : { "user" : "peter" }
    }
}

查询原理

以上面查询语句为例。

  • 先查询出每个分片的前二十条。
  • 将所有分片查询的数据进行聚合排序后截取了10~20之间的数据。
  • 返回给查询客户端。

性能问题

  表面看起来是分页查询,实际上是查询了所有数据,所以fromsize的查询方式,随着数据量的提升,性能消耗不只是线性增长。

例如: 当我们有5个分片时,查询数量就是以5倍的数量级递增。

官方限制

  为了防止性能消耗过大的情况发生,ES官方在2.0版本以后,对from size查询做了条数限制,当from+size>10000时,将会报错。也就是说from size只能在10000条以内进行查询。 例如:当from+size>10000时

{
    "from" : 10000, "size" : 10,
    "query" : {
        "term" : { "user" : "peter" }
    }
}

会报错如下

修改查询限制

但是我们可以通过默认参数index.max_result_window突破这个限制,例如

PUT _settings
{
    "index": {
        "max_result_window": "1000000"
    }
}

此时限制数量修改为了20000,再次查询如下,可以获取结果

而如果from+size>20000还会报错,如下

总结

  修改from size的默认限制是一个方案。但是要根据ES本身集群的性能,以及ES索引中的数据结构,来设置合适的数值。并且还要在保证查询的响应速度。

代码样例

java代码样例

略...

python代码样例

略...

Scroll“深”分页

Scroll说明

  • 相对于from size这种小数据量查询,ES针对大数据量拉取,提供了scroll滚动查询的方式,有效的进行大批量的查询。
  • 注意,这种方式并不是用于实时查询而是用于一次性查询大量数据。
  • 滚动查询会在第一次请求时,生成快照,然后滚动查询快照生成的数据。

Scroll原理

  Scroll是一种游标查询(scroll_id),允许我们初次查询初始化,生成当前索引的快照信息,并返回scroll id,后面根据scroll id分批拉取查询的结果。后续针对索引的任何修改,都不会影响到快照内容。

  查询要发送scroll id以此定位上次的查询位置,并且设置游标的过期时间,并且每次拉取的时候会刷新游标的过期时间。因为游标窗口比较耗费资源,过期时间能够保证es自动释放窗口资源。

请求示例

POST /twitter/_search?scroll=1m
{
    "size": 100,
    "query": {
        "match" : {
            "user" : "peter"
        }
    },
    "sort": ["_doc"]
}

下次查询,直接传入scroll时间和id如下

{
    "scroll" : "1m",
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}

发送参数相同,滚动式的返回剩余结果,所以scroll也叫滚动查询

后续查询,无需修改参数

scroll id可以手动删除,也可以等待自动失效

DELETE /_search/scroll
{
    "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
}

总结

Scroll无法实现实时分页查询,本身它的设计也是为了拉取大量甚至全量的数据。

java代码样例

略...

python代码样例

略...

Search After深分页

search after说明

  search after与scroll类似,都提供了深度分页查询。区别是search after是实时的,弥补了scroll非实时的不足。

  search after的思想是,以上一次的查询结果中用于排序的属性的值,作为下一次的查询条件

测试案例

POST:http://58.87.123.16:9200/twitter/tweet/:_search

{
  "size": 10,
  "query": {"match_all": {}},
  "sort": [
    {"id": "asc"}
  ]
}

全量查询

下次查询,传入上一次的sort值

post:http://58.87.123.16:9200/twitter/tweet/:_search
{
  "size": 10,
  "query": {"match_all": {}},
  "sort": [
    {"id": "asc"}
  ],
  "search_after": ["10"]
}

search after实现了和Scroll一样的滚动查询,区别是searchafter是实时的。

search after更像是条件查询。

java代码样例

略...

python代码样例

略...

扩展

  search after可以指定被sort的属性的search after的值。说白了就是可以指定某一个属性排序,并获取这个属性指定值后面的数据。

  比如我可以直接指定以id排序,search after的值为id=100,size=10,这样我可以获取100~110之间的数据。

但是依然无法满足我们想要实现所有数据的翻页查询的要求。原因如下:

  • 对于search after分页的前提是,要有一个属性能够让查询的数据整体有序,并且我们能够知道每一页这个数据的值。
  • 假设有一个可用于分页的id值,唯一且连续。
  • 因为加了查询条件,所以连续的id也将会变得不连续且未知。
  • 所以你无法计算出当前页的前几页的id值,也无法知道后续页的id值
  • 你只能知道当前页的id值,并去定位下一页的id值。
  • 所以search after既不能向前跳页,也不能向后跳页,只能实时的向后翻页。

方案总结

  从调研结果来看,想要实现灵活的分页查询,只能使用from size的方式。from size可以修改默认限制条数,参数:index.max_result_window。

  • 优点:使用起来与正常翻页没有区别,实现简单。
  • 缺点:性能耗损随着数据增大而增大。

from+size查询样例图

from size获取数据流程如下:

  • 假设当前ES有五个分片。
  • from=10000,size=10000,查询10000~20000之间的数据,相当于从五个分片上各获取两万条。
  • 对获取数据进行聚合排序,并获取聚合排序后的前10000~20000之间的数据。
  • 如果获取的是50000~50010之间的数据,相当于获取了250050的数据进行排序,并拿到50000~50010之间的数据。