Elasticsearch 的 scroll 滚动查询

发布于 2021-06-03 13:12:42 字数 2912 浏览 2447 评论 0

一、如何使用 scroll

滚动并不是为了实时的用户响应,而是为了处理大量的数据,例如为了使用不同的配置来重新索引一个 index 到另一个 index 中去。

client 支持:Perl 和 Python

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

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

POST /company_new/_doc/_search?scroll=1m 
{
    "query": {
        "match_all" : {}
    }
}

使用上面的请求返回的结果中包含一个 scroll_id,这个 ID 可以被传递给 scroll API 来检索下一个批次的结果。


POST /_search/scroll
{
    "scroll" : "1m", 
    "scroll_id" : "c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"

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

注意:初始搜索请求和每个后续滚动请求返回一个新的 _scroll_id,只有最近的 _scroll_id 才能被使用。

二、使用 scroll-scan 的高效滚动

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

scroll API 保持了那些已经返回记录结果,所以能更加高效地返回排序的结果。但是,按照默认设定排序结果仍然需要代价。

一般来说,你仅仅想要找到结果,不关心顺序。你可以通过组合 scroll 和 scan 来关闭任何打分或者排序,以最高效的方式返回结果。你需要做的就是将 search_type=scan 加入到查询的字符串中:

POST /company_new/_doc/_search?scroll=1m&search_type=scan
{
   "query": {
       "match_all" : {}
   }
}

注意:设置 search_type 为 scan 可以关闭打分,让滚动更加高效。

三、保持搜索上下文存活

参数 scroll (传递给 search 请求还有每个 scroll 请求)告诉 Elasticsearch 应该需要保持搜索上下文多久。这个值(比如说 1m)并不需要长到可以处理所有的数据——仅仅需要足够长来处理前一批次的结果。每个 scroll 请求(包含 scroll 参数)设置了一个新的失效时间。

四、清除 scroll API

搜索上下文当 scroll 超时就会自动移除。但是保持 scroll 存活需要代价,所以 scrolls 当scroll不再被使用的时候需要被用 clear-scroll 显式地清除:

DELETE /_search/scroll
{ 
  "scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"]
}

多个 scroll ID 可按照数据传入


DELETE /_search/scroll 
{ 
  "scroll_id" : ["c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1", "aGVuRmV0Y2g7NTsxOnkxaDZ"]
}

有搜索上下文可以通过 _all 参数而清除:

DELETE /_search/scroll/_all

五、Sliced Scroll

对于返回大量文档的滚动查询,可以将滚动分割为多个切片,可以单独使用:

GET /company_new/_search?scroll=1m
{
    "slice": {
        "id": 0, ①//切片的id
        "max": 2 ②//最大切片数
    },
    "query": {
        "match_all" : {}
    }
}
GET /company_new/_search?scroll=1m
{
    "slice": {
        "id": 1,
        "max": 2
    },
    "query": {
        "match_all" : {}
    }
}

每个滚动都是独立的,可以像任何滚动请求一样并行处理。第一个请求的结果返回属于第一个 slice(id:0)的文档,第二个请求的结果返回属于第二个 slice 的文档。由于最大切片数设置为 2,因此两个请求的结果的并集等效于没有切片的滚动查询的结果。默认情况下,首先在 shard 集合上进行分割,然后在每个 shard 上使用 _uid 字段在本地进行分割,其公式如下: slice(doc) = floorMod(hashCode(doc._uid), max) 例如,如果 shard 数等于 2 且用户请求 4 个 slice,则分配片0和2到第一个 shard,切片1和3分配给第二个 shard。

如果 slice 的数量大于 shard 的数量,则切片过滤器在第一次调用时非常慢,它具有 O(N)的复杂度,并且存储器成本等于每个 slice 的 N 倍,其中N是在shard中的文档的总数。在几次调用之后,缓存过滤器在后续调用应该更快,但是您应该限制并行执行的切片查询的数量以避免内存溢出。

默认情况下,每个滚动允许的最大切片数限制为 1024,您可以更新 index.max_slices_per_scroll 索引设置以绕过此限制。

建议 slice=shard,官方文档中建议 max 的值不要超过 shard 的数量,否则可能会导致内存爆炸。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

已经忘了多久

文章 0 评论 0

15867725375

文章 0 评论 0

LonelySnow

文章 0 评论 0

走过海棠暮

文章 0 评论 0

轻许诺言

文章 0 评论 0

信馬由缰

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文