在es中,默认查询的 from + size 数量不能超过一万,官方对于超过1万的处理计划使用游标计划,今日介绍下几种计划,希望对你有用。

数据预备,模拟较大数据量,往es中灌入60w的数据,其中只要2个字段,一个seq,一个timestamp,如下图:

elasticsearch 查询超10000的几种解决方案

计划1:scroll 游标

游标计划中,咱们只需求在第一次拿到游标id,之后经过游标就能唯一确定查询,在这个查询中经过咱们指定的 size 移动游标,详细操作看看下面实操。

kibana

# 检查index的settings
GET demo_scroll/_settings
---
{
  "demo_scroll" : {
    "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "demo_scroll",
        "max_result_window" : "10000", # 窗口1w
        "creation_date" : "1680832840425",
        "number_of_replicas" : "1",
        "uuid" : "OLV5W_D9R-WBUaZ_QbGeWA",
        "version" : {
          "created" : "6082399"
        }
      }
    }
  }
}
---

# 查询 from+size > 10000 的
GET demo_scroll/_search
{
  "sort": [
    {
      "seq": {
        "order": "desc"
      }
    }
  ],
  "from": 9999,
  "size": 10
}
---
{
  "error": {
    "root_cause": [
      {
        "type": "query_phase_execution_exception",
        "reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10009]. 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",
    "reason": "all shards failed",
    "phase": "query",
    "grouped": true,
    "failed_shards": [
      {
        "shard": 0,
        "index": "demo_scroll",
        "node": "7u5oEE-kSoqXlxEHgDZd4A",
        "reason": {
          "type": "query_phase_execution_exception",
          "reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10009]. 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."
        }
      }
    ]
  },
  "status": 500
}
---

# 游标查询,设置游标有效时刻,有效时刻内,游标都能够使用,过期就不行了
GET demo_scroll/_search?scroll=5m
{
  "sort": [
    {
      "seq": {
        "order": "desc"
      }
    }
  ],
  "size": 200
}

上面操作中经过游标的成果回来:

elasticsearch 查询超10000的几种解决方案

仿制_scroll_id到查询窗口中:

GET _search/scroll
{
  "scroll":"5m",
  "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAGc2DFndkTUUwTU9jVFBTZ1lHaVZWMkMzdlEAAAAAABnNhBZ3ZE1FME1PY1RQU2dZR2lWVjJDM3ZRAAAAAAAUCtIWZC0tUHFYdllTbEd5LUg4bnQyRlJVZwAAAAAAGc2FFndkTUUwTU9jVFBTZ1lHaVZWMkMzdlEAAAAAABnNhhZ3ZE1FME1PY1RQU2dZR2lWVjJDM3ZR"
}

以下是回来成果:

elasticsearch 查询超10000的几种解决方案

留意,此时游标移动了,所以咱们能够经过游标的方法不断后移,直到移动到咱们想要的 from+size 规模内。

看看python中完成:

python

def get_docs_by_scroll_v2(index, from_, size, scroll="10m"):
    """
    :param index:
    :param from_:
    :param size:
    :param scroll: scroll timeout, in timeout, the scroll is valid
    :param timeout:
    :return:
    """
    query_scroll = {
        "size": size,
        "sort": {"seq": {"order": "desc"}},
        "_source": ["seq"]
    }
    init_res = es.search(index=index, body=query_scroll, scroll=scroll, timeout=scroll)
    scroll_id = init_res["_scroll_id"]
    ans = []
    for i in range(1, int(from_/size)+1, 1):
        res = es.scroll(scroll_id=scroll_id, scroll=scroll)
        if i == int(from_/size):
            for item in res["hits"]["hits"]:
                ans.append(item["_source"])
    return ans

咱们只需求经过size控制每次游标的移动规模,详细成果看实践需求。

游标确实能够帮咱们获取到超1w的数据,但也有问题,就是假如分页相对深的时分,游标遍历的时刻相对较长,下面介绍别的一种计划。

计划2:设置 max_result_size

在此计划中,咱们建议仅限于测验用,出产禁用,究竟当数据量大的时分,过大的数据量或许导致es的内存溢出,直接崩掉,一年绩效白干。

下面看看操作吧。

kibana

# 调大查询窗口巨细,比方100w
PUT demo_scroll/_settings
{
  "index.max_result_window": "1000000"
}
# 检查查询最大数
GET demo_scroll/_settings
---
{
  "demo_scroll" : {
    "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "demo_scroll",
        "max_result_window" : "1000000",
        "creation_date" : "1680832840425",
        "number_of_replicas" : "1",
        "uuid" : "OLV5W_D9R-WBUaZ_QbGeWA",
        "version" : {
          "created" : "6082399"
        }
      }
    }
  }
}
---
# from+size > 10000 的查询
GET demo_scroll/_search
{
  "from": 12000,
  "size": 20,
  "sort": [
    {
      "seq": {
        "order": "desc"
      }
    }
  ]
}

下图为查询成果:

elasticsearch 查询超10000的几种解决方案

牢记出产慎用,测验可用。

方法3:经过书签方法

用过关系型数据库都知道,假设咱们有个 user 表,其中 id 为自增主键,在相似下面的 SQL 句子中:

select id, name from user limit 100000, 10;

假如你直接用这个句子查询,想想会不会被喷呢。涉及到较为深度的分页时,联络 mysql 的查询原理,首先查到那么多数据,只回来最终几条数据,浅分页影响小,深度分页,当数据量很大时,功能堪忧,所以通常咱们能够这样优化下 SQL 句子:

select id, name from user where id > 100000 limit 10;

经过走索引的规模查找,相较于全表扫描式,功能必定就更好了。

借鉴这种思路,仍是以上面 es 中的索引数据,咱们能够经过限定字段 seq 来作为书签从而分页。

详细思路,已然限制10000,咱们能够10000接着10000去查找,是不是也能够呢,到了最终在10000内的数据,再经过 from+size 就能够处理了。

比方咱们跳过 skip = from/10000, 再接着 from+size

# 在es默认的10000查找规模内,下面查询报错
GET demo_scroll/_search
{
  "from": 11000,
  "size": 20,
  "sort": [
    {
      "seq": {
        "order": "asc"
      }
    }
  ]
}
# 处理上面查询,能够先查10000,在1000+20查询
GET demo_scroll/_search
{
  "size": 10000,
  "sort": [
    {
      "seq": {
        "order": "asc"
      }
    }
  ]
}
GET demo_scroll/_search
{
  "from": 1000, 
  "size": 20,
  "sort": [
    {
      "seq": {
        "order": "asc"
      }
    }
  ],
  "query": {
    "bool": {
      "must": [
        {"range": {
          "seq": {
            "gt": 10000
          }
        }}
      ]
    }
  }
}

当然假如是这个 skip 比较大,比方 from=31000,这个时分 skip=3,咱们能够第一次拿到 seq,然后第二次经过 range 查询 seq > 10000size=10000 保持不变,直到第三次,拿到第三次的 seq30000分位的值,第四次在 from+size 中查询,这种思路在编码完成中能够经过循环完成,详细自己完成,也不难。

经过上面三种方法,咱们就能够完成深度分页下的数据查询,游标方法查多少多能够,首要仍是右边遍历的功能太差,建议经过 书签 方法完成,功能远比游标好,自己项目中的测验环境,数据量在5w内,查询2w多的数据,经过游标需求1min多,经过书签方法需求5秒,你品品。