ES分页的两种方法

Published on 2021-06-15 17:47 in 分类: 随笔 with 狂盗一枝梅
分类: 随笔

本篇文章将会讲解ES两种分页查询方法以及优缺点

注意:以下文章中的命令和java代码均基于ES5.3.0版本

一、from/size分页

官网文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/5.3/search-request-from-size.html

查询的时候带上from和size两个参数即可

  • from参数默认为0,size参数默认为10
  • 查询结果索引 from + size 的值不能超出index.max_result_window,默认该值是10000
  • 如果想做深度分页,可以考虑使用 Scroll 或者Search After 方式。

1.修改默认窗口大小

PUT请求 `{{es-host}}/索引名字/_settings

{
    "index": {
        "max_result_window": 1000000
    }
}

可以适当增大窗口值大小方便常用分页查询

2.JAVA API

transportClient.prepareSearch(索引名)
                .setQuery(mixQuery)
                .setFrom((pageNum - 1) * pageSize)
                .setSize(pageSize)
                .setFetchSource(true);

3.优缺点

优点就是简单,和mysql的分页很相似

缺点也很明显,首先,分页受限于结果窗口大小,而该值默认值仅仅是10000,就算可以调整该分页大小,仍然让人心里不爽快,再者,随着分页from的值越来越大,效率会越来越低。from/size的分页原理是先查询出所有结果,然后根据from和size进行结果截取,所以势必会随着分页深度的增加效率越来越低。最后,由于需要将查询结果都加载到内存,有可能会出现内存爆掉的情况发生。

二、Scroll分页

从from/size的官网为文档上来看,如果需要“深分页”,最好使用Scroll or Search After API。

官网文档:https://www.elastic.co/guide/en/elasticsearch/reference/5.3/search-request-scroll.html

scroll效率之所以高,是因为它内部维护着一个“游标”,类似于指针的东西,每次查询完size个数据,游标就会移动到查询数据的尾部等待下次查询。

1.初次请求

请求方式

POST

请求地址

{{es-host}}/website/_search?scroll=1m

请求体

{
    "size": 1,
    "query": {
        "match_all": {}
    }
}

响应结果

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAANLb6Flg4V2VGRndwUW91SnBVWUlxcTJzc2cAAAAAADS2-RZYOFdlRkZ3cFFvdUpwVVlJcXEyc3NnAAAAAAA0tvgWWDhXZUZGd3BRb3VKcFVZSXFxMnNzZwAAAAAANLb8Flg4V2VGRndwUW91SnBVWUlxcTJzc2cAAAAAADS2-xZYOFdlRkZ3cFFvdUpwVVlJcXEyc3Nn",
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 5,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "website",
                "_type": "blog",
                "_id": "AXoGPweiCycsj4NKux3V",
                "_score": 1.0,
                "_source": {
                    "name": "test"
                }
            }
        ]
    }
}

初次请求和正常的请求没什么区别,就加上一个参数scroll=1m即可,表示本次scroll游标一分钟内有效。

接着,拿到的结果中除了正常返回的数据,还多了一个_scroll_id参数,下次请求就可以直接使用该参数进行分页查询了。

2.以后的请求

请求方式

GET

请求地址

{{es-host}}/_search/scroll?scroll=1m&scroll_id=DnF1ZXJ5VGhlbkZldGNoBQAAAAAANL04FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9NhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtBAAAAAAA0vTcWYmtGNjlWSkVSbTIyNm9zazlUX3VLQQAAAAAANL05FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9OhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtB

请求体

响应结果

{
    "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAANL04FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9NhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtBAAAAAAA0vTcWYmtGNjlWSkVSbTIyNm9zazlUX3VLQQAAAAAANL05FmJrRjY5VkpFUm0yMjZvc2s5VF91S0EAAAAAADS9OhZia0Y2OVZKRVJtMjI2b3NrOVRfdUtB",
    "took": 1,
    "timed_out": false,
    "terminated_early": true,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
        "total": 5,
        "max_score": 1.0,
        "hits": [
            {
                "_index": "website",
                "_type": "blog",
                "_id": "AXlizj8kCycsj4NKuvL8",
                "_score": 1.0,
                "_source": {
                    "name": "cve-2015-1427"
                }
            }
        ]
    }
}

可以看到,再次请求的时候不需要再指定索引名称和相关的复杂查询条件,直接告诉ES上一次查询的游标_scroll_id即可。

3.JAVA API

SearchRequestBuilder searchRequestBuilder = transportClient.prepareSearch(index.split(","))
                .setQuery(mixQuery)
                .setSize(pageSize)
                .setFetchSource(true)
                .setScroll(TimeValue.timeValueMinutes(1))
                .addSort(fieldSortBuilder);

只需要加上 .setFetchSource(true)即可。

第一次查询之后要正常处理结果数据,并非像是某些文章中讲的只是返回scroll_id

之后的查询则就简单多了

scrollId = searchResponse.getScrollId();
        while (searchResponse.getHits().getHits().length > 0) {
//处理结果数据
            SearchScrollRequestBuilder searchScrollRequestBuilder = transportClient.prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(1));
            searchResponse = searchScrollRequestBuilder.execute().actionGet();
            scrollId = searchResponse.getScrollId();
        }

完整示例

        SearchRequestBuilder searchRequestBuilder = transportClient.prepareSearch("")
//                .setQuery(mixQuery)
                .setSize(100)
                .setFetchSource(true)
                .setScroll(TimeValue.timeValueMinutes(1))
                .addSort(SortBuilders.fieldSort("_uid").order(SortOrder.ASC));
        SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
        String scrollId = searchResponse.getScrollId();
        while (searchResponse.getHits().getHits().length > 0) {
            //处理结果数据
            SearchScrollRequestBuilder searchScrollRequestBuilder = transportClient.prepareSearchScroll(scrollId).setScroll(TimeValue.timeValueMinutes(1));
            searchResponse = searchScrollRequestBuilder.execute().actionGet();
            scrollId = searchResponse.getScrollId();
        }

三、Search After分页

文档地址:https://www.elastic.co/guide/en/elasticsearch/reference/5.3/search-request-search-after.html

没用过,略。


#elasticsearch #es
目录
复制 复制成功