Paginate search results | Elasticsearch Guide [8.1] | Elastichttps://www.elastic.co/guide/en/elasticsearch/reference/8.1/paginate-search-results.html#slice-scroll 先敬上官网链接。一手资料永远都是官网!

我先说一下为什么会关注这块内容:我有需求需要快速导出集群中的数据,而数据又非常多。将近20亿!

单线程利用 scroll导出也比较慢。然后我在业务上去拆分任务,虽然能完成,但是非常麻烦。然后又研究了一下网上的帖子,看到有人说滚动查询还可以利用 slice来提升速度。它的快原理其实就是利用多线程。

 接下来再分析一下slice的原理,以及一些优化点!

Slice使用方式以及原理

 它实际上会将一个任务拆分成多个任务。

api

GET /my-index-000001/_search?scroll=1m
{
  "slice": {
    "id": 0,                      
    "max": 2                      
  },
  "query": {
    "match": {
      "message": "foo"
    }
  }
}
GET /my-index-000001/_search?scroll=1m
{
  "slice": {
    "id": 1,
    "max": 2
  },
  "query": {
    "match": {
      "message": "foo"
    }
  }
}

 解释一下:实际上 max就是任务拆分的个数。id就是拆分后对应的任务id,下标从0开始的!

工作原理就是

 拆分在分片上进行。然后数据再切分。切分公式就是:slice(doc) = floorMod(hashCode(doc._id), max)).

解释一下这个公式:其实就是 文档id的hash值去模上最大任务数,得到的余数就是对应拆分的任务。注意这里的任务id是可以换成其它字段,但是有个条件是只能是数字类型。

假如你有10亿数据,你拆分成5个任务。应该是一个任务2亿。最后五个任务执行完,应该是10亿数据的全集!

The result from the first request returned documents that belong to the first slice (id: 0) and the result from the second request returned documents that belong to the second slice. Since the maximum number of slices is set to 2 the union of the results of the two requests is equivalent to the results of a scroll query without slicing. By default the splitting is done first on the shards, then locally on each shard using the _id field. The local splitting follows the formula slice(doc) = floorMod(hashCode(doc._id), max)).

注意点

  • 想要获取极限的速度,要知道这是有成本的。在执行这个的时候,要想清楚,是否继续要对外提供稳定良好服务。根据这个前提,再去适当的调参数到极限能力!
  • 理论值:任务拆分数要小于等于分片数。建议是分片的倍数。假如索引有200个分片,是不是可以搞200个任务。那肯定不能,这是非常花资源的,假如你的机器资源没有那么多,又想稳定对外提供服务,不建议这么搞,我的建议是最大不要超过CPU的核心数。
  • slice 在 search + scroll 的时候可以用,在聚类结果导出的时候也可以用。
  • 注意用完的scroll应该及时删除。es默认保留500个滚动任务id。多了就不能再使用了!
  • 注意scroll状态保留的时间。如果你后续接到数据以后的处理时间比较长,或者查询本身时间花费也长,你应该合理的设置一个长的状态保留时间。
GET /_search?scroll=1m
{
  "sort": [
    "_doc"
  ]
}
  • 假如你的结果不考虑结果顺序性,利用默认的数据在分片中的顺序是一个不错的选择,就是上述的API。
  • 注意在滚动查询的过程中。因为是对段保留了快照。此时新的数据进来,是无法被看到的。另外在滚动查询过程中,会阻碍段合并,因为执行中的任务保留着这些旧段的信息!

实战

官网java客户端,并没有提供 slice的demo,明天我提供一份出来!先测试一下。 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐