关闭

[ElasticSearch]Java API 之 滚动搜索(Scroll API)

标签: ElasticSearch滚动大数据量搜索Scroll APIJava
7751人阅读 评论(3) 收藏 举报
分类:

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

Scroll API的创建并不是为了实时的用户响应,而是为了处理大量的数据(Scrolling is not intended for real time user requests, but rather for processing large amounts of data)。从 scroll 请求返回的结果只是反映了 search 发生那一时刻的索引状态,就像一个快照(The results that are returned from a scroll request reflect the state of the index at the time that the initial search request was made, like a snapshot in time)。后续的对文档的改动(索引、更新或者删除)都只会影响后面的搜索请求。

1. 普通请求

假设我们想一次返回大量数据,下面代码中一次请求58000条数据:

  1.    /**
  2.     *  普通搜索
  3.     * @param client
  4.     */
  5.    public static void search(Client client) {
  6.        String index = "simple-index";
  7.        String type = "simple-type";
  8.        // 搜索条件
  9.        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
  10.        searchRequestBuilder.setIndices(index);
  11.        searchRequestBuilder.setTypes(type);
  12.        searchRequestBuilder.setSize(58000);
  13.        // 执行
  14.        SearchResponse searchResponse = searchRequestBuilder.get();
  15.        // 搜索结果
  16.        SearchHit[] searchHits = searchResponse.getHits().getHits();
  17.        for (SearchHit searchHit : searchHits) {
  18.            String source = searchHit.getSource().toString();
  19.            logger.info("--------- searchByScroll source {}", source);
  20.        } // for
  21.    }

运行结果:

  1. Caused by: QueryPhaseExecutionException[Result window is too large, from + size must be less than or equal to: [10000] but was [58000]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter.]
  2. at org.elasticsearch.search.internal.DefaultSearchContext.preProcess(DefaultSearchContext.java:212)
  3. at org.elasticsearch.search.query.QueryPhase.preProcess(QueryPhase.java:103)
  4. at org.elasticsearch.search.SearchService.createContext(SearchService.java:676)
  5. at org.elasticsearch.search.SearchService.createAndPutContext(SearchService.java:620)
  6. at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:371)
  7. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryTransportHandler.messageReceived(SearchServiceTransportAction.java:368)
  8. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryTransportHandler.messageReceived(SearchServiceTransportAction.java:365)
  9. at org.elasticsearch.transport.TransportRequestHandler.messageReceived(TransportRequestHandler.java:33)
  10. at org.elasticsearch.transport.RequestHandlerRegistry.processMessageReceived(RequestHandlerRegistry.java:75)
  11. at org.elasticsearch.transport.TransportService$4.doRun(TransportService.java:376)
  12. at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
  13. ... 3 more

从上面我们可以知道,搜索请求一次请求最大量为[10000]。我们的请求量已经超标,因此报错,异常信息提示我们请求大数据量的情况下使用Scroll API。

2. 使用Scroll API 请求

为了使用 scroll,初始搜索请求应该在查询中指定 scroll 参数,告诉 Elasticsearch 需要保持搜索的上下文环境多长时间(滚动时间)。

  1. searchRequestBuilder.setScroll(new TimeValue(60000));

下面代码中指定了查询条件以及滚动属性,如滚动的有效时长(使用setScroll()方法)。我们通过SearchResponse对象的getScrollId()方法获取滚动ID。滚动ID会在下一次请求中使用。

  1.    /**
  2.     * 使用scroll进行搜索
  3.     * @param client
  4.     */
  5.    public static String searchByScroll(Client client) {
  6.        String index = "simple-index";
  7.        String type = "simple-type";
  8.        // 搜索条件
  9.        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
  10.        searchRequestBuilder.setIndices(index);
  11.        searchRequestBuilder.setTypes(type);
  12.        searchRequestBuilder.setScroll(new TimeValue(30000));
  13.        // 执行
  14.        SearchResponse searchResponse = searchRequestBuilder.get();
  15.        String scrollId = searchResponse.getScrollId();
  16.        logger.info("--------- searchByScroll scrollID {}", scrollId);
  17.        SearchHit[] searchHits = searchResponse.getHits().getHits();
  18.        for (SearchHit searchHit : searchHits) {
  19.            String source = searchHit.getSource().toString();
  20.            logger.info("--------- searchByScroll source {}", source);
  21.        } // for
  22.        return scrollId;
  23.        
  24.    }

使用上面的请求返回的结果中的滚动ID,这个 ID 可以传递给 scroll API 来检索下一个批次的结果。这一次请求中不用添加索引和类型,这些都指定在了原始的 search 请求中。

每次返回下一个批次结果 直到没有结果返回时停止 即hits数组空时(Each call to the scroll API returns the next batch of results until there are no more results left to return, ie the hits array is empty)。

  1.    /**
  2.     *  通过滚动ID获取文档
  3.     * @param client
  4.     * @param scrollId
  5.     */
  6.    public static void searchByScrollId(Client client, String scrollId){
  7.        TimeValue timeValue = new TimeValue(30000);
  8.        SearchScrollRequestBuilder searchScrollRequestBuilder;
  9.        SearchResponse response;
  10.        // 结果
  11.        while (true) {
  12.            logger.info("--------- searchByScroll scrollID {}", scrollId);
  13.            searchScrollRequestBuilder = client.prepareSearchScroll(scrollId);
  14.            // 重新设定滚动时间
  15.            searchScrollRequestBuilder.setScroll(timeValue);
  16.            // 请求
  17.            response = searchScrollRequestBuilder.get();
  18.            // 每次返回下一个批次结果 直到没有结果返回时停止 即hits数组空时
  19.            if (response.getHits().getHits().length == 0) {
  20.                break;
  21.            } // if
  22.            // 这一批次结果
  23.            SearchHit[] searchHits = response.getHits().getHits();
  24.            for (SearchHit searchHit : searchHits) {
  25.                String source = searchHit.getSource().toString();
  26.                logger.info("--------- searchByScroll source {}", source);
  27.            } // for
  28.            // 只有最近的滚动ID才能被使用
  29.            scrollId = response.getScrollId();
  30.        } // while
  31.    }

备注:

初始搜索请求和每个后续滚动请求返回一个新的 滚动ID——只有最近的滚动ID才能被使用。(The initial search request and each subsequent scroll request returns a new_scroll_id — only the most recent _scroll_id should be used)  

我每次后续滚动请求返回的滚动ID都是相同的,所以对上面的备注,不是很懂,有明白的可以告知,谢谢。


如果超过滚动时间,继续使用该滚动ID搜索数据,则会报错:

  1. Caused by: SearchContextMissingException[No search context found for id [2861]]
  2. at org.elasticsearch.search.SearchService.findContext(SearchService.java:613)
  3. at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:403)
  4. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryScrollTransportHandler.messageReceived(SearchServiceTransportAction.java:384)
  5. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryScrollTransportHandler.messageReceived(SearchServiceTransportAction.java:381)
  6. at org.elasticsearch.transport.TransportRequestHandler.messageReceived(TransportRequestHandler.java:33)
  7. at org.elasticsearch.transport.RequestHandlerRegistry.processMessageReceived(RequestHandlerRegistry.java:75)
  8. at org.elasticsearch.transport.TransportService$4.doRun(TransportService.java:376)
  9. at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
  10. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  11. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  12. at java.lang.Thread.run(Thread.java:745)


3. 清除滚动ID

虽然当滚动有效时间已过,搜索上下文(Search Context)会自动被清除,但是一值保持滚动代价也是很大的,所以当我们不在使用滚动时要尽快使用Clear-Scroll API进行清除。

  1. /**
  2. * 清除滚动ID
  3. * @param client
  4. * @param scrollIdList
  5. * @return
  6. */
  7. public static boolean clearScroll(Client client, List<String> scrollIdList){
  8. ClearScrollRequestBuilder clearScrollRequestBuilder = client.prepareClearScroll();
  9. clearScrollRequestBuilder.setScrollIds(scrollIdList);
  10. ClearScrollResponse response = clearScrollRequestBuilder.get();
  11. return response.isSucceeded();
  12. }
  13. /**
  14. * 清除滚动ID
  15. * @param client
  16. * @param scrollId
  17. * @return
  18. */
  19. public static boolean clearScroll(Client client, String scrollId){
  20. ClearScrollRequestBuilder clearScrollRequestBuilder = client.prepareClearScroll();
  21. clearScrollRequestBuilder.addScrollId(scrollId);
  22. ClearScrollResponse response = clearScrollRequestBuilder.get();
  23. return response.isSucceeded();
  24. }



4. 参考:

https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-scroll.html

http://www.jianshu.com/p/14aa8b09c789

5. 说明

本代码基于ElasticSearch 2.4.1 



4
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

Elasticsearch使用scroll进行分页查询

之前一直使用from size进行分页查询(深度分页),当数据量大的时候,对全部数据进行遍历,使用from size性能会很差,然后了解了下scroll方法,简单讲解一下scroll方法的使用。 从网...
  • lom9357bye
  • lom9357bye
  • 2017-03-21 20:50
  • 5464

elasticsearch-利用游标查询 'Scroll'来做分页查询

游标查询 'Scroll' scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。 游标查询允许我们 先做查询初始化,然...
  • chuan442616909
  • chuan442616909
  • 2017-02-15 14:12
  • 4148

Elasticsearch使用Scroll-Scan实现数据遍历

Elasticsearch 是一个实时的分布式搜索与分析引擎,被广泛用来做全文搜索、结构化搜索、结果分析。在实际应用中有时需要遍历某个索引的全部数据,这时使用分页检索的形式效率会比较差。      ...
  • peterwanghao
  • peterwanghao
  • 2017-07-12 18:12
  • 1868

ElasticSearch Java API之滚动搜索(Scroll API)

原文地址:http://blog.csdn.net/sunnyyoona/article/details/52810397 一般搜索请求都是返回一"页"数据,无论数据量多大都一起返回给用户,...
  • tanga842428
  • tanga842428
  • 2017-07-11 15:14
  • 192

Elasticsearch Java API 的使用(8)—Scroll (游标)API详解

Elasticsearch中进行大数据量查询时,往往因为设备、网络传输问题影响查询数据的效率;Elasticsearch中提供了Scroll(游标)的方式对数据进行少量多批次的查询,来提高查询效率。p...
  • zx711166
  • zx711166
  • 2017-07-14 17:31
  • 75

ElasticSearch48:初识搜索引擎_上机动手实战基于scroll技术滚动搜索大量数据

1.为什么使用srcoll滚动搜索 问题:如果一次性查询出100000条数据,那么性能会很差,此时一般会采用scroll滚动查询,一批一批的查,知道所有数据都查询完。 使用scroll滚动搜索...
  • m0_37557582
  • m0_37557582
  • 2018-01-08 09:19
  • 33

基于Luence的分布式搜索引擎ElasticSearch搜索实例演示(Java API)

安装包下载 当前最新版本为:0.20.6 http://www.elasticsearch.org/download/  官方视频教程 http://www.elasticsearch.org...
  • lijunlinlijunlin
  • lijunlinlijunlin
  • 2015-04-14 00:03
  • 592

分布式搜索elasticsearch java API 之(七)------与MongoDB同步数据

原文:http://blog.csdn.net/laigood/article/details/7691068
  • huanghui036
  • huanghui036
  • 2014-06-27 15:29
  • 729

分布式搜索elasticsearch java API

Mapping,就是对索引库中索引的字段名及其数据类型进行定义,类似于关系数据库中表建立时要定义字段名及其数据类型那样,不过es的mapping比数据库灵活很多,它可以动态添加字段。一般不需要要指定m...
  • shyaideguo
  • shyaideguo
  • 2014-11-12 10:46
  • 383

分布式搜索elasticsearch java API 之(八)------使用More like this实现基于内容的推荐

原文:http://blog.csdn.net/laigood/article/details/7831713
  • huanghui036
  • huanghui036
  • 2014-06-27 15:39
  • 985
    个人资料
    • 访问:1453356次
    • 积分:21978
    • 等级:
    • 排名:第385名
    • 原创:624篇
    • 转载:133篇
    • 译文:60篇
    • 评论:187条
    博客专栏
    文章分类
    最新评论