ES 深度分页scroll使用方式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES 深度分页scroll使用方式相关的知识,希望对你有一定的参考价值。

参考技术A 我们知道ES对于from+size的个数是有限制的,二者之和不能超过1w。当所请求的数据总量大于1w时,可用scroll来代替from+size。

首次查询使用方式如下:

接下来的查询方式如下:

如果你对scroll取出的数据顺序没有要求的话,则可以对“_doc”进行排序,es对这种排序做了优化。使用方式如下:

如果你想通过slice并行分片查询的话,可以这样设置:

两个请求可以同时运行。但是这样做会有个缺陷,内存占用较大,且第一次查询很慢。因为查询是O(N)的复杂度且每个slice占用N个bits,N是shard的总文档数。之后缓存的数据将加快查询。

为了避免上述情况,可以选择一个doc_values做slice,但是必须要确保这个field有以下特性:

在第一次查询时,记录上一次查询的位置,在接下来的查询中获取到上次查询的位置,接着查询。
比如说将查询order by time offset 0 limit 100,改写成order by time where time>0 limit 100,记录最后一个$time_max,接下来的查询order by time offset 100 limit 100,改写成order by time where time>$time_max limit 100。如此往复,可以看出scroll是一个常量查询延迟和开销,并无什么副作用。

关于scroll 和search after的详细信息,请看 http://www.jianshu.com/p/91d03b16af77 .

https://www.elastic.co/guide/en/elasticsearch/reference/5.4/breaking_50_search_changes.html

对应到java api中,可用addSort("_doc", SortOrder.ASC)代替。

http://zcty5v5.xyz/2016/10/17/ES-scroll-issues/

elasticsearch获取大批量数据时 深度分页(from&size) VS scroll游标查询

        在公司的操作es进行查询数据时,es默认分页且只返回十条数据,并且size最大只能传10000,这种查询方式称之为深度分页的方式也就是用 from 和 size 参数分页查询。由于我们当时开发任务紧急,所以在需要获取全量数据时就直接更改了这一限制,改为了10000000(一千万)条。但是这一方式可能在之后项目上线后随着数据量逐渐增多的情况下可能会对es服务造成一定隐患,因此需要改为官方推荐的scroll(游标)方式查询获取全量数据。

       对于深度分页获取大量数据的劣势官方文档已给出了较为详细的解释,并且官方建议使用scroll的方式来进行全量数据的获取。具体如下:

技术图片

摘自  《Elasticsearch: 权威指南》→  基础入门 → 搜索——最基本的工具 →  分页 

技术图片   摘自  《Elasticsearch: 权威指南》→ 基础入门→ 执行分布式检索→ 取回阶段  

附: 《Elasticsearch: 权威指南》-> 链接

        相对于from和size的分页来说,可以把scroll理解为关系型数据库里的 cursor(游标),不是查询所有数据然后剔除不要的部分,而是记录了当前读取的文档信息位置,保证下一次快速继续读取。也可以理解为维护了一份当前索引段的快照信息,这个快照信息是你执行这个scroll查询时的快照。在这个查询后的任何新索引进来的数据,都不会在这个快照中查询到。因此scroll 并不适合用来做实时搜索,而更适用于一次性查询大量的数据(甚至是全部的数据)。

技术图片 摘自  《Elasticsearch: 权威指南》→ 基础入门→ 执行分布式检索→ 游标查询Scroll

        es的删除机制为根据条件查询出来再进行删除操作,在阅读spring-data-elasticsearch包中的elasticsearchTemplate源码时,发现该类中的删除方法也是使用的scroll方式获取了全量数据再进行删除,因此scroll游标的方式查询数据更适用于全量数据的获取。以下为elasticsearchTemplate源码:

 1 @Override
 2     public <T> void delete(DeleteQuery deleteQuery, Class<T> clazz) {
 3 
 4         String indexName = !StringUtils.isEmpty(deleteQuery.getIndex()) ? deleteQuery.getIndex()
 5                 : getPersistentEntityFor(clazz).getIndexName();
 6         String typeName = !StringUtils.isEmpty(deleteQuery.getType()) ? deleteQuery.getType()
 7                 : getPersistentEntityFor(clazz).getIndexType();
 8         Integer pageSize = deleteQuery.getPageSize() != null ? deleteQuery.getPageSize() : 1000;
 9         Long scrollTimeInMillis = deleteQuery.getScrollTimeInMillis() != null ? deleteQuery.getScrollTimeInMillis()
10                 : 10000l;
11 
12         SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(deleteQuery.getQuery()).withIndices(indexName)
13                 .withTypes(typeName).withPageable(PageRequest.of(0, pageSize)).build();
14 
15         SearchResultMapper onlyIdResultMapper = new SearchResultMapper() {
16             @Override
17             public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
18                 List<String> result = new ArrayList<String>();
19                 for (SearchHit searchHit : response.getHits().getHits()) {
20                     String id = searchHit.getId();
21                     result.add(id);
22                 }
23                 if (result.size() > 0) {
24                     return new AggregatedPageImpl<T>((List<T>) result, response.getScrollId());
25                 }
26                 return new AggregatedPageImpl<T>(Collections.EMPTY_LIST, response.getScrollId());
27             }
28         };
29 
30         Page<String> scrolledResult = startScroll(scrollTimeInMillis, searchQuery, String.class, onlyIdResultMapper);
31         BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
32         List<String> ids = new ArrayList<String>();
33 
34         do {
35             ids.addAll(scrolledResult.getContent());
36             scrolledResult = continueScroll(((ScrolledPage<T>)scrolledResult).getScrollId(), scrollTimeInMillis, String.class, onlyIdResultMapper);
37         } while(scrolledResult.getContent().size() != 0);
38 
39         for (String id : ids) {
40             bulkRequestBuilder.add(client.prepareDelete(indexName, typeName, id));
41         }
42 
43         if (bulkRequestBuilder.numberOfActions() > 0) {
44             bulkRequestBuilder.execute().actionGet();
45         }
46 
47         clearScroll(((ScrolledPage<T>) scrolledResult).getScrollId());
48     }

  

  故此记录在查询全量数据时使用scroll游标方式查询数据更好。

                                                       

以上是关于ES 深度分页scroll使用方式的主要内容,如果未能解决你的问题,请参考以下文章

ElasticSearch第5天 es实现分页查询的几种方式

ES实战ES分页与去重

ES实战ES分页与去重

ElasticSearch 深度分页解决方案

架构师成长记_第八周_17_ES- 深度分页

架构师成长记_第八周_17_ES- 深度分页