Elasticsearch分页

1、概述

Elasticsearch中数据都存储在分片中,当执行搜索时每个分片独立搜索后,数据再经过整合返回。ElasticSearch的搜索请求一次请求最大量为10000。如果超过则会发生错误。那么,如果数据量很大,就必须实现分页查询。如果我想查询前10条数据,按照一般的查询流程来说,会经历以下步骤:
(1)客户端请求发给某个节点
(2)节点转发给个个分片,查询每个分片上的前10条
(3)结果返回给节点,整合数据,提取前10条
(4)返回给请求客户端
那么当我想要查询第100条到第200条的数据该怎么办呢?这个时候就用到分页查询了。

2、from-size分页

from-size分页可以理解为简单意义上的分页。它的原理很简单,就是查询前200条数据,然后截断前100条,只返回100-200的数据。这样其实白白浪费了前100条的查询。

查询的方法如:

SearchResponse scrollResp = client.prepareSearch()
                .setSearchType(SearchType.SCAN)
                .setIndices("")
                .setTypes("")
                .setFrom(100)
                .setSize(200)
                .setQuery(QueryBuilders.matchAllQuery())
                .execute().actionGet();

其中,from定义了目标数据的偏移值,size定义当前返回的数目。默认from为0,size为10,即所有的查询默认仅仅返回前10条数据。

越往后的分页,执行的效率越低。它会随着from的增加,消耗时间也会增加。而且数据量越大,效果越明显!也就是说,分页的偏移值越大,执行分页查询时间就会越长!

2、Scroll分页

一般搜索请求默认返回一”页”数据,无论数据量多大都只返回默认的数据给用户,Scroll API可以允许我们检索大量数据(甚至全部数据),它允许我们做一个初始阶段搜索并且持续批量从Elasticsearch里拉取结果直到没有结果剩下。这有点像传统数据库里的cursors(游标)。

相对于from和size的分页来说,使用scroll可以模拟一个传统数据的游标,记录当前读取的文档信息位置。这个分页的用法,不是为了实时查询数据,而是为了一次性查询大量的数据(甚至是全部的数据)。因为这个scroll相当于维护了一份当前索引段的快照信息,这个快照信息是你执行这个scroll查询时的快照。在这个查询后的任何新索引进来的数据,都不会在这个快照中查询到。但是它相对于from和size,不是查询所有数据然后剔除不要的部分,而是记录一个读取的位置,保证下一次快速继续读取。代码如下:

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;

import java.net.InetAddress;

/**
 * elasticSearch-2.3.3
 */
public class ScrollTest {

    private static Client client;

    static {
        try {
            Settings settings = Settings.settingsBuilder().put("cluster.name", "elasticsearch-cluster").build();
            TransportClient transportClient = TransportClient.builder().settings(settings).build();
            transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(""), 9500));
            transportClient.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(""), 9500));
            client = transportClient;
        }catch (Exception e){

        }
    }

    /*
     * 利用from-size分页,获取较小数据量查询
     */
    private void getSearchData(QueryBuilder queryBuilder) {

        SearchResponse searchResponse = client
                .prepareSearch("")
                .setTypes("")
                .setQuery(queryBuilder)
                .setFrom(0)
                .setSize(100)
                .execute()
                .actionGet();

        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit searchHit : searchHits) {
//            Integer id = (Integer) searchHit.getSource().get("id");
//            ids.add(id);
        }
    }

    /*
     * 利用scrollId深度分页,获取大量数据
     */
    private void getSearchDataByScrolls(QueryBuilder queryBuilder) {

        SearchResponse searchResponse = client.prepareSearch()
                .setIndices("")
                .setTypes("")
                .setScroll(TimeValue.timeValueMinutes(1)) //游标维持时间
                .setSearchType(SearchType.SCAN)//用Scan提高性能,但第一次不返回结果,返回scrollId
                .setSize(1000)//实际返回的数量为1000*index的主分片数
                .execute()
                .actionGet();

        TimeValue timeValue = new TimeValue(80000);
        while(true) {
            try {
                //第一次查询,只返回数量和一个scrollId
                //注意第一次运行没有结果
                for (SearchHit hit : searchResponse.getHits().getHits()) {
                    //
                }
                //使用上次的scrollId继续访问
                //初始搜索请求和每个后续滚动请求返回一个新的滚动ID,只有最近的滚动ID才能被使用
                searchResponse = client.prepareSearchScroll(searchResponse.getScrollId())

                if (searchResponse.getHits().getHits().length == 0) {
                    break;
                }
            } catch (Exception e) {

            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值