Elasticsearch 分页问题

Elasticsearch分页一般有三种方式:

1.form和size的方式

2.Scroll api

3.search_after参数

 

from+size分页

按照一般的查询流程来说,如果我想查询前10条数据:

    1 客户端请求发给某个节点
    2 节点转发给个个分片,查询每个分片上的前10条
    3 结果返回给节点,整合数据,提取前10条
    4 返回给请求客户端

该分页方式可以通过from+size的方式来进行实现。
from定义了目标数据的偏移值,size定义当前返回的事件数目。

GET /fs/_search?pretty
{
  "from" : 0 , "size" : 10
}

GET /fs/_search?pretty
{
  "from" : 10 , "size" : 10
}

这种分页方式只适合少量数据,因为随from增大,查询的时间就会越大,而且数据量越大,查询的效率指数下降

优点:from+size在数据量不大的情况下,效率比较高
缺点:在数据量非常大的情况下,from+size分页会把全部记录加载到内存中,这样做不但运行速递特别慢,而且容易让es出现内存不足而挂掉

比如要取第5001页的数据,在分页的时候,elasticsearch需要首先在每一个节点上取出50020的数据,然后和每一个节点的所有数据进行排序,取出排序后在50010到50020的数据,然后返回。这样随着数据量的增大,每次分页时排序的开销会越来越大。

一般的分页需求我们可以使用form和size的方式实现,但是这种分页方式在深度分页的场景下应该是要避免使用的。深度分页会随着请求的页次增加,所消耗的内存和时间的增长也是成比例的增加,为了避免深度分页产生的问题,elasticsearch从2.0版本开始,增加了一个限制:

index.max_result_window =10000

 

scroll深分页

为了解决上面的问题,elasticsearch提出了一个scroll滚动的方式,这个滚动的方式原理就是通过每次查询后,返回一个scroll_id。根据这个scroll_id 进行下一页的查询。可以把这个scroll_id理解为通常关系型数据库中的游标。

scroll就是维护了当前索引段的一份快照信息–缓存(这个快照信息是你执行这个scroll查询时的快照)

可以把 scroll 分为初始化和遍历两步:

1、初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照;

GET fs/_search?scroll=3m
{
  "query": {"match_all": {}},
   "size": 3
}

初始化的时候就像是普通的search一样
其中的scroll=3m代表当前查询的数据缓存3分钟
Size:3 代表当前查询3条数据

2、遍历时,从这个快照里取数据;
在遍历时候,拿到上一次遍历中的_scroll_id,然后带scroll参数,重复上一次的遍历步骤,直到返回的数据为空,表示遍历完成。
每次都要传参数scroll,刷新搜索结果的缓存时间,另外不需要指定index和type(不要把缓存的时时间设置太长,占用内存)。

scroll虽然能够解决from size带来的问题,但是由于它代表的是某个时刻的snapshot,不适合做实时查询;且由于scroll后接超时时间,频繁地发起scroll请求,也会出现一系列问题。

 

search_after参数

search_after: 性能优秀,类似于优化后的分页查询,历史条件过滤掉数据。

检索第一页的查询如下所示:

 POST twitter/_search
    {
        "size": 10,
        "query": {
            "match" : {
                "title" : "elasticsearch"
            }
        },
        "sort": [
            {"date": "asc"},
            {"_id": "desc"}
        ]
    }

    每个文档具有一个唯一值的字段应该用作排序规范的仲裁器。否则,具有相同排序值的文档的排序顺序将是未定义的。建议的方法是使用字段_id,它肯定包含每个文档的一个唯一值。

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

 GET twitter/_search
    {
        "size": 10,
        "query": {
            "match" : {
                "title" : "elasticsearch"
            }
        },
        "search_after": [1463538857, "654323"],
        "sort": [
            {"date": "asc"},
            {"_id": "desc"}
        ]
    }

    当我们使用 search_after 参数的时候,from参数必须被设置成 0 或 -1 (当然你也可以不设置这个from参数)。

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

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章