写点什么

京东面试题:ElasticSearch 深度分页解决方案

  • 2022 年 5 月 12 日
  • 本文字数:3135 字

    阅读完需:约 10 分钟

Scroll


======


?



Scroll 遍历数据



?


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


这个分页的用法,「不是为了实时查询数据」,而是为了**「一次性查询大量的数据(甚至是全部的数据」**)。


因为这个 scroll 相当于维护了一份当前索引段的快照信息,这个快照信息是你执行这个 scroll 查询时的快照。在这个查询后的任何新索引进来的数据,都不会在这个快照中查询到。


但是它相对于 from 和 size,不是查询所有数据然后剔除不要的部分,而是记录一个读取的位置,保证下一次快速继续读取。


不考虑排序的时候,可以结合 SearchType.SCAN 使用。


scroll 可以分为初始化和遍历两部,初始化时将**「所有符合搜索条件的搜索结果缓存起来(注意,这里只是缓存的 doc_id,而并不是真的缓存了所有的文档数据,取数据是在 fetch 阶段完成的)」**,可以想象成快照。


在遍历时,从这个快照里取数据,也就是说,在初始化后,对索引插入、删除、更新数据都不会影响遍历结果。


「基本使用」


POST /twitter/tweet/_search?scroll=1m


{


"size": 100,


"query": {


"match" : {


"title" : "elasticsearch"


}


}


}


初始化指明 index 和 type,然后,加上参数 scroll,表示暂存搜索结果的时间,其它就像一个普通的 search 请求一样。


会返回一个_scroll_id,_scroll_id 用来下次取数据用。


「遍历」


POST /_search?scroll=1m


{


"scroll_id":"XXXXXXXXXXXXXXXXXXXXXXX I am scroll id XXXXXXXXXXXXXXX"


}


这里的 scroll_id 即 上一次遍历取回的_scroll_id 或者是初始化返回的_scroll_id,同样的,需要带 scroll 参数。


重复这一步骤,直到返回的数据为空,即遍历完成。


「注意,每次都要传参数 scroll,刷新搜索结果的缓存时间」。另外,「不需要指定 index 和 type」


设置 scroll 的时候,需要使搜索结果缓存到下一次遍历完成,「同时,也不能太长,毕竟空间有限。」


「优缺点」


缺点:


  1. 「scroll_id 会占用大量的资源(特别是排序的请求)」

  2. 同样的,scroll 后接超时时间,频繁的发起 scroll 请求,会出现一些列问题。

  3. 「是生成的历史快照,对于数据的变更不会反映到快照上。」


「优点:」


适用于非实时处理大量数据的情况,比如要进行数据迁移或者索引变更之类的。


Scroll Scan


===========


ES 提供了 scroll scan 方式进一步提高遍历性能,但是 scroll scan 不支持排序,因此 scroll scan 适合不需要排序的场景


「基本使用」


Scroll Scan 的遍历与普通 Scroll 一样,初始化存在一点差别。


POST /my_index/my_type/_search?search_type=scan&scroll=1m&size=50


{


"query": { "match_all": {}}


}


需要指明参数:


  • search_type:赋值为 scan,表示采用 Scroll Scan 的方式遍历,同时告诉 Elasticsearch 搜索结果不需要排序。

  • scroll:同上,传时间。

  • size:与普通的 size 不同,这个 size 表示的是每个 shard 返回的 size 数,最终结果最大为?number_of_shards * size。


「Scroll Scan 与 Scroll 的区别」


  1. Scroll-Scan 结果**「没有排序」**,按 index 顺序返回,没有排序,可以提高取数据性能。

  2. 初始化时只返回?_scroll_id,没有具体的 hits 结果

  3. size 控制的是每个分片的返回的数据量,而不是整个请求返回的数据量。


Sliced Scroll


=============


如果你数据量很大,用 Scroll 遍历数据那确实是接受不了,现在 Scroll 接口可以并发来进行数据遍历了。


每个 Scroll 请求,可以分成多个 Slice 请求,可以理解为切片,各 Slice 独立并行,比用 Scroll 遍历要快很多倍。


POST /index/type/_search?scroll=1m


{


"query": { "match_all": {}},


"slice": {


"id": 0,


"max": 5


}


}


POST ip:port/index/type/_search?scroll=1m


{


"query": { "match_all": {}},


"slice": {


"id": 1,


"max": 5


}


}


上边的示例可以单独请求两块数据,最终五块数据合并的结果与直接 scroll scan 相同。


其中 max 是分块数,id 是第几块。


?



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



?


Search After


============


Search_after 是 ES 5 新引入的一种分页查询机制,其原理几乎就是和 scroll 一样,因此代码也几乎是一样的。


「基本使用:」


第一步:


POST twitter/_search


{


"size": 10,


"query": {


"match" : {


"title" : "es"


}


},


"sort": [


{"date": "asc"},


{"_id": "desc"}


]


}


返回出的结果信息 :


{


"took" : 29,


"timed_out" : false,


"_shards" : {


"total" : 1,


"successful" : 1,


"skipped" : 0,


"failed" : 0


},


"hits" : {


"total" : {


"value" : 5,


"relation" : "eq"


},


"max_score" : null,


"hits" : [


{


...


},


"sort" : [


...


]


},


{


...


},


"sort" : [


124648691,


"624812"


]


}


]


}


}


上面的请求会为每一个文档返回一个包含 sort 排序值的数组。


这些 sort 排序值可以被用于 search_ 《一线大厂 Java 面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 after 参数里以便抓取下一页的数据。


比如,我们可以使用最后的一个文档的 sort 排序值,将它传递给 search_after 参数:


GET twitter/_search


{


"size": 10,


"query": {


"match" : {


"title" : "es"


}


},


"search_after": [124648691, "624812"],


"sort": [


{"date": "asc"},


{"_id": "desc"}


]


}


若我们想接着上次读取的结果进行读取下一页数据,第二次查询在第一次查询时的语句基础上添加 search_after,并指明从哪个数据后开始读取。


「基本原理」


es 维护一个实时游标,它以上一次查询的最后一条记录为游标,方便对下一页的查询,它是一个无状态的查询,因此每次查询的都是最新的数据。


由于它采用记录作为游标,因此**「SearchAfter 要求 doc 中至少有一条全局唯一变量(每个文档具有一个唯一值的字段应该用作排序规范)」**


「优缺点」


「优点:」


  1. 无状态查询,可以防止在查询过程中,数据的变更无法及时反映到查询中。

  2. 不需要维护 scroll_id,不需要维护快照,因此可以避免消耗大量的资源。


「缺点:」


  1. 由于无状态查询,因此在查询期间的变更可能会导致跨页面的不一值。

  2. 排序顺序可能会在执行期间发生变化,具体取决于索引的更新和删除。

  3. 至少需要制定一个唯一的不重复字段来排序。

  4. 它不适用于大幅度跳页查询,或者全量导出,对第 N 页的跳转查询相当于对 es 不断重复的执行 N 次 search after,而全量导出则是在短时间内执行大量的重复查询。


SEARCH_AFTER 不是自由跳转到任意页面的解决方案,而是并行滚动多个查询的解决方案。


总结


==


分页方式性能优点缺点场景 from + size 低灵活性好,实现简单深度分页问题数据量比较小,能容忍深度分页问题 scroll 中解决了深度分页问题无法反应数据的实时性(快照版本)维护成本高,需要维护一个 scroll_id 海量数据的导出需要查询海量结果集的数据 search_after 高性能最好不存在深度分页问题能够反映数据的实时变更实现复杂,需要有一个全局唯一的字段连续分页的实现会比较复杂,因为每一次查询都需要上次查询的结果,它不适用于大幅度跳页查询海量数据的分页


ES7 版本变更


=======


参照:https://www.elastic.co/guide/en/elasticsearch/reference/master/paginate-search-results.html#scroll-search-results


在 7.*版本中,ES 官方不再推荐使用 Scroll 方法来进行深分页,而是推荐使用带 PIT 的 search_after 来进行查询;


从 7.*版本开始,您可以使用 SEARCH_AFTER 参数通过上一页中的一组排序值检索下一页命中。


使用 SEARCH_AFTER 需要多个具有相同查询和排序值的搜索请求。


如果这些请求之间发生刷新,则结果的顺序可能会更改,从而导致页面之间的结果不一致。


为防止出现这种情况,您可以创建一个时间点(PIT)来在搜索过程中保留当前索引状态。


POST /my-index-000001/_pit?keep_alive=1m


返回一个 PIT ID:


{


"id": "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA=="


}

用户头像

还未添加个人签名 2022.04.13 加入

还未添加个人简介

评论

发布
暂无评论
京东面试题:ElasticSearch深度分页解决方案_Java_爱好编程进阶_InfoQ写作社区