ElasticSearch分页与深度分页问题解决

ElasticSearch分页与深度分页问题解决

ElasticSearch分页

POST movies/_search
{
  "from": 10000,
  "size": 1,
  "query": {
    "match_all": {

    }
  }
}

这是ElasticSearch最简单的分页查询,但以上命令是会报错的。

报错信息,指window默认是10000。

"root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."
      }
    ],
    "type": "search_phase_execution_exception",

怎么解决这个问题,首先能想到的就是调大这个window。

PUT movies/_settings
{ 
    "index" : { 
        "max_result_window" : 20000
    }
}

但这种方法只是暂时解决问题,当数据量越来越大,分页也越来越深,还是会出问题的。

ElasticSearch在分布式系统中的深度分页

理解为什么深度分页是有问题的,我们可以假设在一个有 4 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 40 个结果排序得到全部结果的前 10 个。

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

可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 10000 个结果的原因。

ElasticSearch分页与深度分页问题解决_第1张图片

ElasticSearch深度分页解决方法

Search After

实时获取下一页文档信息

  • 不支持指定页数
  • 只能往下翻

假设Size 10,当查询990-1000,它通过唯一排序值定位,将每次要处理的文档数都控制在10。

ElasticSearch分页与深度分页问题解决_第2张图片

如何使用?举个例子

  1. 插入示例数据

    POST users/_bulk
    { "index" : { } }
    { "name":"user10","age":10}
    { "index" : { } }
    { "name":"user11","age":11}
    { "index" : { } }
    { "name":"user12","age":12}
    { "index" : { } }
    { "name":"user13","age":13}
  2. 查询第一页

    POST users/_search
    {
        "size": 1,
        "query": {
            "match_all": {}
        },
        "sort": [
            {"age": "desc"} ,
            {"_id": "asc"}    
        ]
    }

    这里返回数据

    {
      "took" : 0,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 5,
          "relation" : "eq"
        },
        "max_score" : null,
        "hits" : [
          {
            "_index" : "users",
            "_type" : "_doc",
            "_id" : "sP8mOG0BISxFcLcZlTXF",
            "_score" : null,
            "_source" : {
              "name" : "user13",
              "age" : 13
            },
            "sort" : [
              13,
              "sP8mOG0BISxFcLcZlTXF"
            ]
          }
        ]
      }
    }
  3. 拿到上一页sort信息,放入search_after,往下翻

    POST users/_search
    {
        "size": 1,
        "query": {
            "match_all": {}
        },
        "search_after":
            [
              13,
              "sP8mOG0BISxFcLcZlTXF"],
        "sort": [
            {"age": "desc"} ,
            {"_id": "asc"}    
        ]
    }

Scroll

创建一个快照,所以当新的数据写入时,无法被查询到。

需要每次查询后,输入上一次的Scroll id。

如何使用?举个例子

  1. 插入示例数据

    可以使用search-after例子上的数据

  2. 查询第一页

    POST /users/_search?scroll=5m
    {
        "size": 1,
        "query": {
            "match_all" : {
            }
        }
    }
  3. 拿到scroll_id后,往下翻。不同于search_after,接下去第三页、第四页继续使用这个id即可。

    POST /_search/scroll
    {
        "scroll" : "5m",
        "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAWAWbWdoQXR2d3ZUd2kzSThwVTh4bVE0QQ=="
    }
  4. 查询当前有多少scroll,并删除

    为了防止因打开太多卷轴而导致的问题,不允许用户打开滚动超过某个限制。默认情况下,打开的滚动的最大数量为500.可以使用search.max_open_scroll_context群集设置更新此限制。

    获取有多少个scroll

    GET /_nodes/stats/indices/search

    删除所有的scroll或者根据scroll_id删除

    DELETE /_search/scroll
    {
        "scroll_id" : [
          "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ==",
          "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAABFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAAAxZrUllkUVlCa1NqNmRMaUhiQlZkMWFBAAAAAAAAAAIWa1JZZFFZQmtTajZkTGlIYkJWZDFhQQAAAAAAAAAFFmtSWWRRWUJrU2o2ZExpSGJCVmQxYUEAAAAAAAAABBZrUllkUVlCa1NqNmRMaUhiQlZkMWFB"
        ]
    }
    DELETE /_search/scroll
    {
        "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU1QQ=="
    }
    DELETE /_search/scroll/_all

总结

  • 传统方式(from&size)

    需要实时获取顶部的部分文档。例如查询最新的订单。

  • Scroll

    需要全部文档,例如导出全部数据

  • Search After

    需要做到深度分页

附录

  • ElasticSearch search after官方文档
  • ElasticSearch scroll官方文档

你可能感兴趣的:(elasticsearch)