es from+size scroll search_after 分页查询解析

  1. From+Size
    1.1 基本用法
    查出第 6~8 笔数据

     GET /my_index/_search
     {
       "from": 5,
       "size": 3
     }
    
  • from:表示记录开始的顺序,默认0
  • size:表示取回几笔数据,默认10
  • from+size 默认不能超过10000,可以通过修改索引参数index.max_result_window调整

1.2 基本原理

  • 协调节点将查询请求发送给所有分片
  • 各分片收到请求后,查出 from + size 的数据,并返回给协调节点。例如:当前请求是查询第5~10笔数据,from + size = 10,因此每个分片都要返回排序后的前10笔数据
  • 协调节点将收到的数据进行合并,重新排序,然后返回指定的size分页数据给客户端

1.3 存在的问题

  • 深分页的情况下,也就是from + size 值特别大,可能会造成慢查询。因为每个节点都需要返回 from + size 的排序数据,然后最终还需要合并以及排序,就算没有内存溢出,对CPU和IO也有巨大影响。
为了解决这种问题,ES官方推荐使用 scroll 或者 search_after
  1. Scroll
    2.1 基本用法
    第一步,初始化 scroll ,并获取第一批数据:

     POST /my_index/_search?scroll=5m
     {
       "size":2,
       "query": {
         "match_all": {}
       }, 
       "sort": [
         {
           "num": {
             "order": "asc"
           }
         }
       ]
     }
    
  • scroll=5m:指定查询上下文的有效期,这边设置为5分钟
  • size:指定初始化分页大小,这边设置2
  • sort:排序
返回值如下:
{
  "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFDFaQ3hQblVCNEZuWkdyWGFCcDFnAAAAAADGUE0WclVEQkh0VUlSWnFFM05QUEJaMU5Sdw==",
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "_doc",
        "_id" : "pJBvPnUB4FnZGrXaoStR",
        "_score" : null,
        "_source" : {
          "num" : 1
        },
        "sort" : [
          1
        ]
      },
      {
        "_index" : "my_index",
        "_type" : "_doc",
        "_id" : "pZBvPnUB4FnZGrXaoStR",
        "_score" : null,
        "_source" : {
          "num" : 2
        },
        "sort" : [
          2
        ]
      }
    ]
  }
}
  • 注意:返回值的第一项是_scroll_id,后面的每一步查询都需要传递该参数

第二步,获取下一页数据:

POST /_search/scroll
{
  "scroll": "5m",
  "scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFDFaQ3hQblVCNEZuWkdyWGFCcDFnAAAAAADGUE0WclVEQkh0VUlSWnFFM05QUEJaMU5Sdw=="
}
  • scroll:指定查询上下文的有效期,这边仍然设置为5分钟。注意:这步执行完毕后,查询上下文的有效期将重新计算。可以理解为scroll有效期的设置指的是2次查询的时间间隔,如果超过该有效期没有任务查询发生,那将会失效。
  • scroll_id:传递最近一次查询返回的_scroll_id

2.2 基本原理

  • scroll 初始化会返回所有符合条件的数据,并形成一份快照数据
  • 每次获取分页数据,都会以类似游标的方式读取该快照
    快照的有效期由scroll参数指定,每次查询重新传递scroll参数,会刷新有效期
  • 快照形成后,ES索引数据的更新都不会被 scroll 读取到,这边读取的仍然是快照生成时的数据。因此,不适用于实时数据要求非常高的场景。

2.3 存在的问题

  • 不建议同时打开大量的 scroll 上下文,因为在 scroll 有效期到达之前,都会占用一定的内存空间。官方默认同时打开的 scroll 数量是500,可以通过 search.max_open_scroll_context参数调整
  • scroll 上下文指的是第一次初始化形成的快照,后续基于该上下文的查询都不会查到最新数据。因此不适用于实时性高的场景。
为了避免这些问题,官方推出另一个查询方式:Search After
  1. Search After
    3.1 基本用法
    第一步:构造数据:
    第二步:查出第1页数据,按num+name进行组合排序

     GET test_json/_search
     {
       "query": {
         "bool": {
           "filter": [
             {
               "term": {
                 "status": "309"
               }
             }
           ]
         }
       },
       "size": 2,
       "from": 0,
       "sort": [
         {
          
           "_id": {
             "order": "desc"
           }
         }
       ]
     }
    
    结果如下:
     {
       "took" : 2,
       "timed_out" : false,
       "_shards" : {
         "total" : 1,
         "successful" : 1,
         "skipped" : 0,
         "failed" : 0
       },
       "hits" : {
         "total" : {
           "value" : 181,
           "relation" : "eq"
         },
         "max_score" : null,
         "hits" : [
           {
             "_index" : "test_json",
             "_type" : "_doc",
             "_id" : "yjqdNHgBXcRYjwgstiZS",
             "_score" : null,
             "_source" : {
               "size" : 0,
               "clientip" : "127.0.0.1",
               "responsetime" : 0.0,
               "domain" : "localhost",
               "host" : "127.0.0.1",
               "status" : "309",
               "upstreamtime" : "-",
               "url" : "/index.html",
               "xff" : "-",
               "http_host" : "localhost",
               "@timestamp" : "2019-10-12T10:41:48.000Z",
               "referer" : "-",
               "@version" : "1",
               "path" : "/opt/logs/access.log",
               "upstreamhost" : "-"
             },
             "sort" : [
               "yjqdNHgBXcRYjwgstiZS"
             ]
           },
           {
             "_index" : "test_json",
             "_type" : "_doc",
             "_id" : "yTqdNHgBXcRYjwgstiZS",
             "_score" : null,
             "_source" : {
               "size" : 0,
               "clientip" : "127.0.0.1",
               "responsetime" : 0.0,
               "domain" : "localhost",
               "host" : "127.0.0.1",
               "status" : "309",
               "upstreamtime" : "-",
               "url" : "/index.html",
               "xff" : "-",
               "http_host" : "localhost",
               "@timestamp" : "2019-10-12T10:41:48.000Z",
               "referer" : "-",
               "@version" : "1",
               "path" : "/opt/logs/access.log",
               "upstreamhost" : "-"
             },
             "sort" : [
               "yTqdNHgBXcRYjwgstiZS"
             ]
           }
         ]
       }
     }
    

    第三步:取出最后一笔数据的sort值,传到search_after进行查询

     GET test_json/_search
     {
       "query": {
         "bool": {
           "filter": [
             {
               "term": {
                 "status": "309"
               }
             }
           ]
         }
       },
       "size": 2,
       "from": 0,
       "search_after": [
         "yjqdNHgBXcRYjwgstiZS"
       ],
       "sort": [
         {
           "_id": {
             "order": "desc"
           }
         }
       ]
     }
    
    结果如下:
     {
       "took" : 3,
       "timed_out" : false,
       "_shards" : {
         "total" : 1,
         "successful" : 1,
         "skipped" : 0,
         "failed" : 0
       },
       "hits" : {
         "total" : {
           "value" : 181,
           "relation" : "eq"
         },
         "max_score" : null,
         "hits" : [
           {
             "_index" : "test_json",
             "_type" : "_doc",
             "_id" : "yTqdNHgBXcRYjwgstiZS",
             "_score" : null,
             "_source" : {
               "size" : 0,
               "clientip" : "127.0.0.1",
               "responsetime" : 0.0,
               "domain" : "localhost",
               "host" : "127.0.0.1",
               "status" : "309",
               "upstreamtime" : "-",
               "url" : "/index.html",
               "xff" : "-",
               "http_host" : "localhost",
               "@timestamp" : "2019-10-12T10:41:48.000Z",
               "referer" : "-",
               "@version" : "1",
               "path" : "/opt/logs/access.log",
               "upstreamhost" : "-"
             },
             "sort" : [
               "yTqdNHgBXcRYjwgstiZS"
             ]
           },
           {
             "_index" : "test_json",
             "_type" : "_doc",
             "_id" : "yDqdNHgBXcRYjwgstiZS",
             "_score" : null,
             "_source" : {
               "size" : 0,
               "clientip" : "127.0.0.1",
               "responsetime" : 0.0,
               "domain" : "localhost",
               "host" : "127.0.0.1",
               "status" : "309",
               "upstreamtime" : "-",
               "url" : "/index.html",
               "xff" : "-",
               "http_host" : "localhost",
               "@timestamp" : "2019-10-12T10:41:48.000Z",
               "referer" : "-",
               "@version" : "1",
               "path" : "/opt/logs/access.log",
               "upstreamhost" : "-"
             },
             "sort" : [
               "yDqdNHgBXcRYjwgstiZS"
             ]
           }
         ]
       }
     }
    

3.2 基本原理

  • search after 在排序的基础上,基于上一笔的sort值,查询排在它之后的数据,以此来实现分页
  • search after 是无状态的,它总是查询ES索引最新的数据,这一点跟 scroll 查询完全相反

3.3 存在的问题

  • sort 排序组合必须是唯一的,否则会出现数据丢失的情况。例如上面的例子,[2,“lisi”] 这个组合有2笔数据,但是第二页直接从[3,“wangwu”]开始了。建议是加上id字段作为排序字段。
  1. 总结
  • From+Size 适合浅分页
  • Scroll 适合深分页,并且数据实时要求不高,最适合离线场景
  • Search After 适合深分页

————————————————
版权声明:本文为CSDN博主「FlashHobby」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_28834355/article/details/109143795

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值