Elasticsearch:使用游标查询scroll 实现深度分页

2022-07-18 15:43:31 浏览数 (1)

对于大量的数据而言,我们尽量避免使用 from size 这种方法。这里的原因是 index.max_result_window 的默认值是 10000,也就是说 from size 的最大值是1万。搜索请求占用堆内存和时间与 from size 成比例,这限制了内存。

假如你想 hit 从 990 到 1000,那么每个 shard 至少需要 1000 个文档。

使用 from and size 的深度分页,是非常低效的,因为排序的结果必须从每个分片上取出并重新排序最后返回 10 条。这个过程需要对每个请求页重复。

scroll API 保持了那些结果已经返回的记录,所以能更加高效地返回排序的结果。

本文,我们将讲述了如何运用 scroll 接口来对大量数据来进行有效地分页。

◆  一、游标查询 scroll

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

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

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

深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话,查询结果的成本就会很低。游标查询默认用字段 _doc 来排序。这个指令让 Elasticsearch 仅仅从还有结果的分片返回下一批结果。

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

可以把 scroll 理解为关系型数据库里的 cursor,因此,scroll 并不适合用来做实时搜索,而更适用于后台批处理任务,比如群发。

scroll 具体分为初始化和遍历两步:

  • 初始化时将所有符合搜索条件的搜索结果缓存起来,可以想象成快照;
  • 在遍历时,从这个快照里取数据;

也就是说,在初始化后对索引插入、删除、更新数据都不会影响遍历结果。游标可以增加性能的原因,是因为如果做深分页,每次搜索都必须重新排序,非常浪费,使用scroll就是一次把要用的数据都排完了,分批取出,因此比使用from size还好。

◆  二、scroll 操作示例

注意:从 scroll 请求返回的结果反映了 search 发生时刻的索引状态,就像一个快照。后续的对文档的改动(索引、更新或者删除)都只会影响后面的搜索请求。

为了使用 scroll,初始搜索请求应该在查询中指定 scroll 参数,这可以告诉 Elasticsearch 需要保持搜索的上下文环境多久,如 ?scroll=5m。

下面的DSL 查询命令,使用order_id 进行排序,保持游标查询窗口5分钟。

代码语言:javascript复制
GET kibana_sample_data_ecommerce/_search?scroll=5m
{ "query": { "match_all": {}}, "sort" : ["order_id"], "size": 1000
}

这个查询的返回结果包括一个字段 _scroll_id, 它是一个base64编码的长字符串,如图所示:

现在我们能传递字段 _scroll_id 到 _search/scroll 查询接口获取下一批结果。

每次对 scroll API 的调用返回了结果的下一个批次,直到没有更多的结果返回,也就是直到 hits 数组空了。

代码语言:javascript复制
GET _search/scroll
{ "scroll": "5m", "scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFmV0cjE1Q3JfUmpXeF95NVlyVDlFUncAAAAAAACmmBZTc29KeGx0dFEyYUo2VTVEVWMtdnF3"}

注意再次设置游标查询过期时间为5分钟。

这个游标查询返回的下一批结果。尽管我们指定字段 size 的值为1000,我们有可能取到超过这个值数量的文档。当查询的时候, 字段 size 作用于单个分片,所以每个批次实际返回的文档数量最大为 size * number_of_primary_shards。

这里说的是,从所有分片(N个)里每个拿1000,然后按条件排序,然后按scroll的方式每次返回1000给你,一共能scroll N次。

注意游标查询每次返回一个新字段 _scroll_id。每次我们做下一次游标查询, 我们必须把前一次查询返回的字段 _scroll_id 传递进去。当没有更多的结果返回的时候,我们就处理完所有匹配的文档了。

如果完成此过程,则需要清理上下文,因为上下文在超时之前仍会占用计算资源。如下面的DSL 命令所示,可以使用 scroll_id 参数在 DELETE API 中指定一个或多个上下文:

代码语言:javascript复制
DELETE _search/scroll{ "scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFmV0cjE1Q3JfUmpXeF95NVlyVDlFUncAAAAAAACmmBZTc29KeGx0dFEyYUo2VTVEVWMtdnF3"}

◆  三、和Spring Boot整合示例代码

如果需要查询大量的数据,可以考虑使用 Search Scroll API,这是一种更加高效的方式。还可以和Spring Boot 整合使用,参考如下示例代码:

代码语言:javascript复制
final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
SearchRequest searchRequest = new SearchRequest("kibana_sample_data_ecommerce");
searchRequest.scroll(scroll);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchQuery("title", "Elasticsearch"));
searchRequest.source(searchSourceBuilder);

SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
String scrollId = searchResponse.getScrollId();
SearchHit[] searchHits = searchResponse.getHits().getHits();while (searchHits != null && searchHits.length > 0) {
 SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
 scrollRequest.scroll(scroll);
 searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
 scrollId = searchResponse.getScrollId();
 searchHits = searchResponse.getHits().getHits();
}

ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
boolean succeeded = clearScrollResponse.isSucceeded();

除了第一次查询外,后续的查询都需要携带scrollId,可以理解为游标,用它来控制分页。和from size模式中页码是一个作用。

查询结束后,需要使用client.clearScroll() 方法清除 scroll。

使用scroll api就无法实现跳页查询了,因为除了第一次查询外的其它查询都要依赖上一次查询返回的scrollId,这一点需要注意。

来源:

https://www.toutiao.com/article/7103451700878443041/?log_from=ac4a7d751d6ca_1657597450247

“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com

来都来了,走啥走,留个言呗~

 IT大咖说  |  关于版权

由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!

感谢您对IT大咖说的热心支持!

  • 相关推荐 推荐文章
  • 我经常使用的3种有用的设计模式
  • 快速高效搭建可视化拖拽平台,含事件机制、弹窗等解决方案
  • md5算法不可逆,为啥网上很多网站声称可以解密md5
  • CENTOS断更之后,该何去何从?
  • Nginx 面试题 40 问
  • 你都30多岁的程序员了,还不懂Docker的原理及构建部署过程吗?
  • 一次性把Docker的概念、容器与虚拟机的区别、容器交付的优势讲清
  • 从 CPU 讲起,深入理解 Java 内存模型!
  • 前端工程化:保姆级教学 Jenkins 部署前端项目

0 人点赞