Result window is too large, from + size must be less than or equal to: [10000] but was

场景

做分页查询,当分页达到一定量的时候,报如下错误:

Resultwindowis too large, from+ size must be less than or equal to: [10000] but was [78020]. See the scroll api for a more efficient way to request large data sets. This limit can be setby changing the [index.max_result_window] index level setting.

原因分析: es对from + size的大小进行限制,必须小于等于10000。

解决方案:

方案一(有风险)

将max_result_window参数阈值调大,在业务中限制分页大小,使from+size<=10000;

具体操作

改法一:

动态更改索引设置,为max_result_window参数赋值足够大的值,此处改法有两种,具体如下:

基于特定索引生效:

PUT <index_name>/_settings
{
  "index.max_result_window": <number>
} 

基于全局生效配置:

PUT _all/_settings
{
  "index.max_result_window": <number>
}
改法二:

修改配置文件elasticsearch.yml,增加下列配置,并重启elasticsearch。

index.max_result_window: 100000000

其中改法一只当前有效,elasticsearch重启失效,改法二重启后生效。

PS:这种改法的确能解决眼前问题(网上教程普遍做法),但是会带来严重的后果,得结合业务去考虑,如果你的业务索引库存储数据量大,即单个文档字段多,那么随意改动最大结果窗口配置最常见的就是后期会有OOM现象,而且很难发现原因。

问题分析

正常情况下ES的分页代码如实下面这样的

GET order_2290w/_search
{
  "from": 0,
  "size": 5
}

返回结果

看查询结果我们可以得知,这是一个查询结果集前5条数据的一个检索,即查询第一页的5条数据。返回结果图中数字2即返回的五条文档数据。

如何触发我们标题中的错误,自然是要查询足够大分页的数据啦,达到什么程度呢?当from + size大于10000的时候,就会出现问题,如下图报错信息所示:

报错信息的解释为当前查询的结果超过了10000的最大值。那么疑问就来了,明明只查询了5条数据,为什么它计算最大值要加上我from的数量呢?这里有一个字很关键:“前”,前10000条意味着什么?

ES的from+size查询机制

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

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

max_result_window参数的具体含义

max_result_window是分页返回的最大数值,默认值为10000。max_result_window本身是对JVM的一种保护机制,通过设定一个合理的阈值,避免初学者分页查询时由于单页数据过大而导致OOM。在很多业务场景中经常需要查询10000条以后的数据,当遇到不能查询10000条以后的数据的问题之后,网上的很多答案会告诉你可以通过放开这个参数的限制,将其配置为100万,甚至1000万就行。但是如果仅仅放开这个参数就行,那么这个参数限制的意义有何在呢?如果你不知道这个参数的意义,很可能导致的后果就是频繁的发生OOM而且很难找到原因,设置一个合理的大小是需要通过你的各项指标参数来衡量确定的,比如你用户量、数据量、物理内存的大小、分片的数量等等。通过监控数据和分析各项指标从而确定一个最佳值,并非越大约好。

方案二:官方推荐的解决方案 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 解决大型搜索引擎场景下深度分页问题

不支持向前搜索,只能向后执行

每次只能向后搜索1页数据

以百度为例,默认加载10页数据,假设每页数据为 10条,其实对于ES而言,还不涉及深度分页问题,因为只是查询了一百条数据。

如果项目并发请求并不高,可以单次查询 100 条数据。或者采用懒加载的方式异步请求前十页数据。后面的数据采用 search after 向后翻页即可。

方案三:scroll查询

ES支持scroll滚屏查询,有兴趣的同学可以了解一下,网上相关的文档不少。不过根据ES官网的描述,scroll查询是很耗性能的方式,不建议在实时查询中运用。官方已不推荐使用滚动查询进行深度分页查询,因为无法保存索引状态。

适合场景

单个滚动搜索请求中检索大量结果。

为了使用滚动,初始搜索请求应该scroll在查询字符串中指定参数,该 参数告诉 Elasticsearch 应该保持“搜索上下文”多长时间,例如?scroll=1m。结果如下:


{
  "_scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAABVWsWN3Q4dDJjcVVRQ0NBbllGMmFqN0ZVZw==",  
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 21921750,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      ...
    ]
  }
}
BASH 复制 全屏

上述请求的结果包含一个_scroll_id,应将其传递给scrollAPI 以检索下一批结果。

滚动返回在初始搜索请求时与搜索匹配的所有文档。它会忽略对这些文档的任何后续更改。该scroll_id标识一个搜索上下文它记录身边的一切Elasticsearch需要返回正确的文件。搜索上下文由初始请求创建,并由后续请求保持活动状态

GET <index>/_search?scroll=1m
{
  "size": 100
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值