实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页

这篇具有很好参考价值的文章主要介绍了实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页

问题引入

我们在平常使用ElasticSearch构建查询条件的时候一般用的都是from+size的方式进行分页查询,但是如果我们的页数太深/页面大小太大(from*size)>10000就会引发一个错误,我们将会得到一个错误
实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页
这是为什么呢?
因为ES的分页查询其实是这样来的
因为ElasticSeach的天生分布式的原因,我们的数据是分散在几个分片中的,而我们设置了from+size需要对全部数据进行查询,ES就以下面这种方式进行了查询

Query阶段

实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页

  1. Client 发送一次搜索请求,node1 接收到请求,然后,node1 创建一个大小为 from + size的优先级队列用来存结果,我们管 node1 叫 coordinating node。
  2. coordinating node将请求广播到涉及到的 shards,每个 shard 在内部执行搜索请求,然后,将结果存到内部的大小同样为 from + size 的优先级队列里,可以把优先级队列理解为一个包含 top N结果的列表。
  3. 每个 shard 把暂存在自身优先级队列里的数据返回给 coordinating node,coordinating node 拿到各个 shards 返回的结果后对结果进行一次合并,产生一个全局的优先级队列,存到自身的优先级队列里。

在上面的例子中,coordinating node 拿到(from + size) * 6条数据,然后合并并排序后选择前面的from + size条数据存到优先级队列,以便 fetch 阶段使用。

Fetch阶段

query 阶段知道了要取哪些数据,但是并没有取具体的数据,这就是 fetch 阶段要做的。
实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页

  1. coordinating node 发送 GET 请求到相关shards。

  2. shard 根据 doc 的 _id取到数据详情,然后返回给 coordinating node。

  3. coordinating node 返回数据给 Client。

coordinating node 的优先级队列里有from + size 个_doc _id,但是,在 fetch 阶段,并不需要取回所有数据,在上面的例子中,前100条数据是不需要取的,只需要取优先级队列里的第101到110条数据即可。

需要取的数据可能在不同分片,也可能在同一分片,coordinating node 使用 「multi-get」来避免多次去同一分片取数据,从而提高性能。

这种方式请求深度分页是有问题的:

我们可以假设在一个有 5 个主分片的索引中搜索。当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点,协调节点对 50 个结果排序得到全部结果的前 10 个。

现在假设我们请求第 1000 页—结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。

对结果排序的成本随分页的深度成指数上升。

实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页

如何解决深度分页

分页方式 scorll

es 提供了 scroll 的方式进行分页读取。原理上是对某次查询生成一个游标 scroll_id , 后续的查询只需要根据这个游标去取数据,直到结果集中返回的 hits 字段为空,就表示遍历结束。scroll_id 的生成可以理解为建立了一个临时的历史快照,在此之后的增删改查等操作不会影响到这个快照的结果。

使用 curl 进行分页读取过程如下:

  1. 先获取第一个 scroll_id,url 参数包括 /index/_type/ 和 scroll,scroll 字段指定了scroll_id 的有效生存期,以分钟为单位,过期之后会被es 自动清理。如果文档不需要特定排序,可以指定按照文档创建的时间返回会使迭代更高效。
#返回结果
{
    "_scroll_id": "cXVlcnlBbmRGZXRjaDsxOzk1ODg3NDpTQzRmWWkwQ1Q1bUlwMjc0WmdIX2ZnOzA7",
    "took": 106,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    },
    "hits": {
        "total": 22424,
        "max_score": 1.0,
        "hits": [{
                "_index": "product",
                "_type": "info",
                "_id": "did-519392_pdid-2010",
                "_score": 1.0,
                "_routing": "519392",
                "_source": {
                    ....
                }
            }
        ]
    }
}

  1. 后续的文档读取上一次查询返回的scroll_id 来不断的取下一页,如果srcoll_id 的生存期很长,那么每次返回的 scroll_id 都是一样的,直到该 scroll_id 过期,才会返回一个新的 scroll_id。请求指定的 scroll_id 时就不需要 /index/_type 等信息了。每读取一页都会重新设置 scroll_id 的生存时间,所以这个时间只需要满足读取当前页就可以,不需要满足读取所有的数据的时间,1 分钟就够了

  2. 所有文档获取完毕之后,需要手动清理掉 scroll_id 。虽然es 会有自动清理机制,但是 srcoll_id 的存在会耗费大量的资源来保存一份当前查询结果集映像,并且会占用文件描述符。所以用完之后要及时清理。使用 es 提供的 CLEAR_API 来删除指定的 scroll_id

scroll + scan
当 scroll 的文档不需要排序时,es 为了提高检索的效率,在 2.0 版本提供了 scroll + scan 的方式。随后又在 2.1.0 版本去掉了 scan 的使用,直接将该优化合入了 scroll 中。由于moa 线上的 es 版本是2.3 的,所以只简单提一下。使用的 scan 的方式是指定 search_type=scan

# 2.0-beta 版本禁用 scroll 的排序,使遍历更加高效
[root@dnsserver ~]# curl 'xxx.xxx.xxx.xxx:9200/order/info/_search?scroll=1m&search_type=scan'  -d '{"query":{"match_all":{}}'

缺点:滚动aapi的方式不适合实时查询,其根本还是因为他的快照机制限制了实时性,你可以理解成他是mysql某一时刻的快照,后续的改动在这个scroll存活的时间里是不可见的

search_after 的方式

上述的 scroll search 的方式,官方的建议并不是用于实时的请求,因为每一个 scroll_id 不仅会占用大量的资源(特别是排序的请求),而且是生成的历史快照,对于数据的变更不会反映到快照上。这种方式往往用于非实时处理大量数据的情况,比如要进行数据迁移或者索引变更之类的。那么在实时情况下如果处理深度分页的问题呢?es 给出了 search_after 的方式,这是在 >= 5.0 版本才提供的功能。

search_after 分页的方式和 scroll 有一些显著的区别,首先它是根据上一页的最后一条数据来确定下一页的位置,同时在分页请求的过程中,如果有索引数据的增删改查,这些变更也会实时的反映到游标上。

为了找到每一页最后一条数据,每个文档必须有一个全局唯一值,这种分页方式其实和目前 moa 内存中使用rbtree 分页的原理一样,官方推荐使用 _uid 作为全局唯一值,其实使用业务层的 id 也可以。

  1. 第一页的请求和正常的请求一样,
curl -XGET xxx.xxx.xxx.xxx:9200/order/info/_search
{
    "size": 10,
    "query": {
        "term" : {
            "did" : 519390
        }
    },
    "sort": [
        {"uid": "desc"}
    ]
}
  1. 第二页的请求,使用第一页返回结果的最后一个数据的值,加上 search_after 字段来取下一页。注意,使用 search_after 的时候要将 from 置为 0 或 -1
curl -XGET xxx.xxx.xxx.xxx:9200/order/info/_search
{
    "size": 10,
    "query": {
        "term" : {
            "did" : 519390
        }
    },
    "search_after": [1463538857],  //加上了这个 
    "sort": [
        {"_uid": "desc"}
    ]
}

优点:实时性高,但是无法跳页,较为复杂

是否能用Redis来维护这个uid

实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页

存在什么问题

1.多人操作下导致的并发写问题:
实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页
2. 无法进行跳页

如何解决

多人操作下导致的并发写问题:我们可以静态维护/加一个独立字段/使用主键id,不在redis里面维护
跳页问题:如果我们的数据的顺序不会改变的话,其实我们是可以根据分页大小和分页数去计算出来我们的上一页的最后一条数据的uid/主键id的

但是这种情况基本没有可能出现,因为我们现在的大多数业务都是经过不同的查询条件(名字是否包含)/(修改时间倒序)/(按照男女筛选)因为各种筛选机制,或者如果你是Sass服务的话,不同公司的数据其实是放在一张表的,数据的id可能是交叉的,因此你不能动态的去计算这个uid/主键id作为下一页申请的srot字段,这就是深度分页中跳页的问题,也是search_after方法无法解决的问题

请教大佬

针对这种情况,是否有大佬能给一个解决思路呢,跪求!!!!
如果有什么方案,记得评论扣我,或者私聊哦

资料参考:

京东面试题:ElasticSearch深度分页解决方案 文章来源地址https://www.toymoban.com/news/detail-426411.html

到了这里,关于实习成长之路:关于ElasticSearch深度分页带来的思考,如何解决深度分页和跳页的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • ElasticSearch - DSL查询文档语法,以及深度分页问题、解决方案

    ElasticSearch - DSL查询文档语法,以及深度分页问题、解决方案

    目录 一、DSL 查询文档语法 前言 1.1、DSL Query 基本语法 1.2、全文检索查询 1.2.1、match 查询 1.2.2、multi_match 1.3、精确查询 1.3.1、term 查询 1.3.2、range 查询 1.4、地理查询 1.4.1、geo_bounding_box 1.4.2、geo_distance 1.5、复合查询 1.5.1、相关性算分 1.5.2、function_score 1.5.3、boolean query 1.6、搜索

    2024年02月07日
    浏览(12)
  • Java客户端调用elasticsearch进行深度分页查询 (search_after)

    Java客户端调用elasticsearch进行深度分页查询 (search_after)

    前言 这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱 具体的Search_after解释,可以看我这篇文章 elasticsearch 深度分页查询 Search_after(图文教程) 参考:https://blog.csdn.net/qq_44056652/article/details/126341810 作者:神的孩子

    2024年03月22日
    浏览(8)
  • Elasticsearch 使用scroll滚动技术实现大数据量搜索、深度分页问题 和 search

    基于scroll滚动技术实现大数据量搜索 如果一次性要查出来比如10万条数据,那么性能会很差,此时一般会采取用scroll滚动查询,一批一批的查,直到所有数据都查询完为止。 scroll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜

    2024年02月14日
    浏览(8)
  • 我的“测试开发”成长之路

    我的“测试开发”成长之路

    我相信,有很多测试人员会不断问自己,自己到底要不要坚持做测试,测试的职业发展到底怎么样?如果你还在迷茫,在到处找各种大牛问类似的问题,我希望这篇文章,你看完能够结束你的这个烦恼,给你更多的指明方向,当然也有更多的压力。        这个问题,就像

    2024年02月02日
    浏览(9)
  • Dockerfile成长之路

    Dockerfile成长之路

    随着业务架构的整改,针对非容器化业务全部进行容器化改造,这就设计到了java写的业务代码构建业务镜像,并通过k8s发版,因此,就得学习如何使用dockerfile构建后端业务镜像,可能不止构建后端代码镜像,例如前端写的代码也有可能构建为镜像。还有可能就是要在原有镜像基础上进

    2024年01月24日
    浏览(40)
  • Android程序员成长之路

    Android程序员成长之路

    应该热爱学习Android知识 具备基本的自学能力和解决问题的能力 具备实践能力 Java(基本) C/C++(进阶) Kotlin(基本) Python(可选) 飞书学习路线图 学习路线图正在完善中... 当然读者也可以提出宝贵建议。 我将会按照 Android学习路线图 发布博客文章。 因本人才疏学浅,博客文章中难

    2024年02月09日
    浏览(9)
  • Spring成长之路—Spring MVC

    Spring成长之路—Spring MVC

            在分享SpringMVC之前,我们先对MVC有个基本的了解。MVC(Model-View-Controller)指的是一种软件思想,它将软件分为三层:模型层、视图层、控制层         模型层即Model: 负责处理具体的业务和封装实体类,我们所知的service层、pojo都属于模型层         视图层即view:

    2024年01月21日
    浏览(6)
  • 一个女程序员的成长之路

    一个女程序员的成长之路

    2013年大学毕业了,带着迷茫与好玩,我还年轻的心态,开始在郑州寻觅工作机会,最后很荣幸的在一家小公司入职了,工作的内容是给种植大棚的用户打电话,推销农药。每天就是在网上各种农业平台上面找号码,打电话, 一天拨打电话的在四十个左右,却累的都说不出话

    2024年02月14日
    浏览(10)
  • 助力成长的开源项目 —— 筑梦之路

    助力成长的开源项目 —— 筑梦之路

    免费的闯关式 SQL 自学教程网站,从 0 到 1 带大家掌握常用 SQL 语法,目前一共有 30 多个关卡,希望你在通关的时候,变身为一个 SQL 高手。除了闯关模式之外,这个项目支持自由选择关卡、自定义关卡、SQL 在线练习广场等功能,方便自由地查漏补缺。 GitHub 地址→GitHub - li

    2024年02月03日
    浏览(9)
  • JAVA开发(JAVA架构师成长之路)

    JAVA开发(JAVA架构师成长之路)

    从一个最基础的JAVA开发人员成为JAVA架构师,需要经历8层能力的进阶。 第一阶段:熟悉JAVA基础语法,学会写各种if else和流程语句,熟练使用各种数据类型,集合。能依葫芦画瓢,模仿别人的代码结构,新增类,修改类的信息和逻辑。这个阶段大概是一年的经验。 第二阶段:

    2024年02月09日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包