前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elasticsearch:使用游标查询scroll 实现深度分页

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

作者头像
IT大咖说
发布2022-07-18 15:43:31
3.9K0
发布2022-07-18 15:43:31
举报
文章被收录于专栏:IT大咖说IT大咖说

对于大量的数据而言,我们尽量避免使用 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大咖说的热心支持!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT大咖说 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ◆  一、游标查询 scroll
  • ◆  二、scroll 操作示例
  • ◆  三、和Spring Boot整合示例代码
相关产品与服务
Elasticsearch Service
腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档