• es深度分页查询


    前言

        近期在做新的项目时,使用了ElasticSearch作为数据的存储和查询。接到了一个比较恶心的需求,需要对es进行分页查询,单次查询一万条,最多需要查询十次。当时也没想太多,需求评审时并没有及时反驳,既然掉坑里了,那就想办法爬出来吧!

    es的分页

    1)from+size浅分页

        我们当时有点想当然了,以为from+size就可以搞定(业务代码写多的后果)。实际测试的时候,发现内存消耗特别大,而且速度也很一般。ES的查询机制如下:

        假设我们的ES有三个节点,当分页查询请求过来时,如果落到node1节点,那么node1节点将会向node2和node3发送同样的查询请求,每个节点将topN的文档返回(这里只返回文档的id以及打分排序的字段,减少数据传输),node1会对三个节点的所有文档(3*N个)进行排序,取topN后再根据文档的id到对应的节点上查询整个文档数据,最后返回客户端。

        而对于分页查询,比如from=10000,szie=10000,其实每个节点需要查询from+size=20000条数据,排序之后截取后10000条数据。当我们进行深度分页,比如查询第十页数据时,每个节点需要查询10*size=10W条数据,这个太恐怖了。而且默认情况下,当from+size大于10000时,查询会抛出一个异常,ES2.0后有一个max_result_window属性的设置,默认值是10000,也就是from+size的最大限度。当然你可以修改这个值作为临时的应对策略,不过治标不治本,产品也只会变本加厉!

    2)scroll查询

        ES支持scroll滚屏查询,有兴趣的同学可以了解一下,网上相关的文档不少。不过根据ES官网的描述,scroll查询是很耗性能的方式,不建议在实时查询中运用。摘抄自官网:The Scroll api is recommended for efficient deep scrolling but scroll contexts are costly and it is not recommended to use it for real time user requests.

    3)search_after查询

        search_after是ES5.0及之后版本提供的新特性,search_after有点类似scroll,但是和scroll又不一样,它提供一个活动的游标,通过上一次查询最后一条数据来进行下一次查询。

        比如第一次查询如下:

    GET zm/recall/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": [
        {
          "lastModifyTime": {
            "order": "desc"
          }
        }
      ], 
      "size": 10
    }

        这里根据更新时间进行排序,拿到的结果如下:

    {
            "_index": "zmrecall",
            "_type": "recall",
            "_id": "60310505115909",
            "_score": null,
            "_source": {
              "userId": 60310505115909,
              "score": 1,
              "city": [
                276
              ],
              "sex": 1,
              "age": 29,
              "lastModifyTime": 1545037514
            },
            "sort": [
              1545037514
            ]
          }

        注意到返回结果中有一个sort字段,所以下一次查询的时候,只需要将本次查询最后一条数据中的排序字段加入查询即可:

    GET zm/recall/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": [
        {
          "lastModifyTime": {
            "order": "desc"
          }
        }
      ], 
      "search_after": [1545037514],//这个值与上次查询最后一条数据的sort值一致,支持多个
      "size": 10
    }

        这里需要说明一下,使用search_after查询需要将from设置为0或-1,当然你也可以不写

        另外在ES6.5的文档中有这样一句话需要注意一下:

        大致的意思就是,如果search_after中的关键字为654,那么654323的文档也会被搜索到,所以在选择search_after的排序字段时需要谨慎,可以使用比如文档的id或者时间戳等

        另外,search_after并不是随机的查询某一页数据,而是并行的滚屏查询;search_after的查询顺序会在更新和删除时发生变化,也就是说支持实时的数据查询

    总结

        在使用一个工具之前,一定要对其内部的基本流程有一个简单的了解,否则出现问题时不知从何入手。search_after相比较上面的浅分页以及scroll滚屏查询会有很大的性能提升,推荐大家使用!

    最后附上ES的官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/6.5/search-request-search-after.html

    另外在csdn上也看到一个博客,讲的很不错:https://blog.csdn.net/ctwy291314/article/details/82754652

  • 相关阅读:
    java枚举
    [bzoj3436]小K的农场【差分约束系统】【判负环】
    [bzoj1085][SCOI2005]骑士精神【暴力】
    [bzoj1034][ZJOI2008]泡泡堂BNB【贪心】
    [bzoj1046][HAOI2007]上升序列【dp】
    [bzoj1050][HAOI2006]旅行comf【MST】
    [bzoj1047][HAOI2007]理想的正方形【单调队列】
    [bzoj1004][HNOI2008]Cards【群论】
    [bzoj1045][HAOI2008] 糖果传递【构造】
    [bzoj4589]Hard Nim【FWT】
  • 原文地址:https://www.cnblogs.com/1ning/p/10132703.html
Copyright © 2020-2023  润新知