es深度分页解决方案 - 内含代码复制即用

es系列导航

Elasticsearch第一篇:基本介绍
Elasticsearch第二篇:es版本比较
Elasticsearch第三篇:es的三种分页方式与优劣势比较
Elasticsearch第四篇:java实现Scroll分页,内含代码复制即用

1. 为什么要使用Scroll进行深分页

1.from&size查询在数据量过大的时候,查询效率与数据量成反比

2.ES为了避免深分页,默认不允许使用分页(from&size)查询10000条以后的数据

from/size的原理:es是基于分片的,假设有5个分片,from=100,size=10。则会根据排序规则从5个分片中各取回100 (100+10)条数据数据,然后汇总成500 550条数据后选择最后面的10条数据。越往后的分页,执行的效率越低。总体上会随着from的增加,消耗时间也会增加。而且数据量越大,就越明显!
ps:感谢热心网友的指正,阿里嘎多

2. java实现深分页

话不多说,直接上核心代码

 			// 索引库
            String RANKING_LIKE_INDEX = "xx——索引库名称";
            SearchRequest searchRequest = new SearchRequest(RANKING_LIKE_INDEX);
            // 查询条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            // 查询条件
            searchSourceBuilder.query(QueryBuilders.termQuery("date", "2021-06-08"));
            searchRequest.source(searchSourceBuilder);
        	// 设置滚动查询过期时间 5分钟
            Scroll scroll = new Scroll(TimeValue.timeValueMinutes(5));
            searchRequest.scroll(scroll);

            SearchResponse searchResponse = null;
            try {
                // 查询es信息
                searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                log.error("es连接异常", e);
            }
            // scrollId循环获取结果
            while (searchResponse.getHits().getHits().length != 0) {
                // 设置滚动查询参数
                SearchScrollRequest scrollRequest = new SearchScrollRequest(searchResponse.getScrollId());
                scrollRequest.scroll(scroll);
                try {
                    // 通过ScrollId进行滚动查询
                    searchResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
                } catch (IOException e) {
                    log.error("es连接异常", e);
                }
            }
         	// 清除 Scroll 连接
            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.addScrollId(searchResponse.getScrollId());
            try {
                restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                log.error("ES clear Scroll 连接失败", e);
            }

如果你觉得代码还行,想在复制在本地试一试的话,在本文的最下方有完整的demo。可以复制即用 - 从引包到实现都给你准备好了。

3.Scroll介绍

1.效率更高,资源占用更小

scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。

2.需要先进行初始化

游标查询允许我们 先做查询初始化,然后再批量地拉取结果。 这有点儿像传统数据库中的 cursor

3.拉取后数据,索引上的任何变化会被它忽略

游标查询会取某个时间点的快照数据。 查询初始化之后索引上的任何变化会被它忽略。 它通过保存旧的数据文件来实现这个特性,结果就像保留初始化时的索引 视图 一样。

4.需要设置过期时间,并且在使用完成后要通过代码立即释放掉资源

启用游标查询可以通过在查询的时候设置参数 scroll 的值为我们期望的游标查询的过期时间。 游标查询的过期时间会在每次做查询的时候刷新,所以这个时间只需要足够处理当前批的结果就可以了,而不是处理查询结果的所有文档的所需时间。 这个过期时间的参数很重要,因为保持这个游标查询窗口需要消耗资源,所以我们期望如果不再需要维护这种资源就该直接释放掉,而不是等Elasticsearch来帮我们进行释放。 当然,设置这个超时也能够让 Elasticsearch 在稍后空闲的时候自动释放这部分资源。

4. 完整代码,复制即用


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author shuai
 * @date 2021/6/4 15:32
 */
@Service
@Slf4j
public class WxVideoSyncService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    public void searchByScroll() {
            // 索引
            String RANKING_LIKE_INDEX = "xx——索引库名称";
            SearchRequest searchRequest = new SearchRequest(RANKING_LIKE_INDEX);
            // 查询条件
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            // 查询条件
            searchSourceBuilder.query(QueryBuilders.termQuery("date", "2021-06-08"));
            // 设置排序内容,默认为ASC
            searchSourceBuilder.sort("name", SortOrder.DESC);
            // 设置返回条数,默认10条
            searchSourceBuilder.size(100);
            searchRequest.source(searchSourceBuilder);
        	// 设置滚动查询过期时间 5分钟
            Scroll scroll = new Scroll(TimeValue.timeValueMinutes(5));
            searchRequest.scroll(scroll);

            SearchResponse searchResponse = null;
            try {
                // 查询es信息
                searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            } catch (IOException e) {
                log.error("es连接异常", e);
            }
        
            if (searchResponseIsNotNull(searchResponse)) {
                this.buildResponse(searchResponse);
            }
        
            // scrollId循环获取结果
            while (searchResponseIsNotNull(searchResponse)) {
                // 设置滚动查询参数
                SearchScrollRequest scrollRequest = new SearchScrollRequest(searchResponse.getScrollId());
                scrollRequest.scroll(scroll);
                try {
                    // 通过ScrollId进行滚动查询
                    searchResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
                } catch (IOException e) {
                    log.error("es连接异常", e);
                }
                this.buildResponse(searchResponse);
            }
        
            this.clearScroll(searchResponse.getScrollId());
    }
	 /**
     * 判断返回内容是否为空
     */
    private boolean searchResponseIsNotNull(SearchResponse searchResponse) {
        return !Objects.isNull(searchResponse)
                && !Objects.isNull(searchResponse.getHits())
                && !Objects.isNull(searchResponse.getHits().getHits())
                && searchResponse.getHits().getHits().length > 0
                && searchResponse.getHits().getTotalHits() > 0;
    }

	 /**
     * 处理信息
     */
    private ArrayList<TestDTO> buildResponse(SearchResponse searchResponse) {
        if (searchResponseIsNotNull(searchResponse)) {
            SearchHit[] hits = searchResponse.getHits().getHits();
            // 实体转换
            return Arrays.stream(hits)
                    .map(hit -> JSON.parseObject(hit.getSourceRef().utf8ToString(), TestDTO.class))
                    .collect(Collectors.toList());
        }
        return null;
    }

    /**
    * 关闭Scroll链接
    */
	private void clearScroll(String scrollId) {
        // 清除 Scroll 连接
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
        clearScrollRequest.addScrollId(scrollId);
        try {
            restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("ES clear Scroll 连接失败", e);
        }
    }
 
}

官方DEMO : https://www.elastic.co/guide/en/elasticsearch/client/java-api/2.4/java-search-scrolling.html

  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值