elasticsearch查询仅返回10000条数据的解决方法

elasticsearch

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

elasticsearch默认输出最多一万条
查询第10001条数据开始就会报错:Result window is too large, from + size must be less than or equal to:[10000] but was [XXXXX]
究竟是为要做这样一个限制,于是就查阅了资料。

最后通过查阅了解到出现这个问题是由于ElasticSearch的默认 深度翻页 机制的限制造成的。ES默认的分页机制一个不足的地方是,比如有5010条数据,当你仅想取第5000到5010条数据的时候,ES也会将前5000条数据加载到内存当中,所以ES为了避免用户的过大分页请求造成ES服务所在机器内存溢出,默认对深度分页的条数进行了限制,默认的最大条数是10000条,这是正是问题描述中当获取第10000条数据的时候报Result window is too large异常的原因。

一、查询阶段:

在初始化查询阶段(query phase),查询被向索引中的每个分片副本(原本或副本)广播。每个分片在本地执行搜索并且建立了匹配document的优先队列(priority queue)。

优先队列

一个优先队列(priority queue is)只是一个存有前n个(top-n)匹配document的有序列表。这个优先队列的大小由分

页参数from和size决定。例如,下面这个例子中的搜索请求要求优先队列要能够容纳100个document

GET /_search
{
"from": 90,
"size": 10
}

这个查询的过程被描述在图分布式搜索查询阶段中。

ES为什么不支持一次查询10000条以后的数据?_第1张图片

查询阶段包含以下三步:
1.客户端发送一个 search(搜索)  请求给 Node 3  , Node 3  创建了一个长度为 from+size  的空优先级队列。

2. Node 3  转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果到一个大小为 from+size  的有序本地优先队列里去。

3.每个分片返回document的ID和它优先队列里的所有document的排序值给协调节点 Node 3  。 Node 3  把这些值合并到自己的优先队列里产生全局排序结果。


    当一个搜索请求被发送到一个节点Node,这个节点就变成了协调节点。这个节点的工作是向所有相关的分片广播搜索请求并且把它们的响应整合成一个全局的有序结果集。这个结果集会被返回给客户端。
    第一步是向索引里的每个节点的分片副本广播请求。就像document的 GET  请求一样,搜索请求可以被每个分片的原本或任意副本处理。这就是更多的副本(当结合更多的硬件时)如何提高搜索的吞吐量的方法。对于后续请求,协调节点会轮询所有的分片副本以分摊负载。
     每一个分片在本地执行查询和建立一个长度为 from+size  的有序优先队列——这个长度意味着它自己的结果数量就足够满足全局的请求要求。分片返回一个轻量级的结果列表给协调节点。只包含documentID值和排序需要用到的值,例如 _score  。协调节点将这些分片级的结果合并到自己的有序优先队列里。这个就代表了最终的全局有序结果集。到这里,查询阶段结束。

注意:
一个索引可以由一个或多个原始分片组成,所以一个对于单个索引的搜索请求也需要能够把来自多个分片的结果组合
起来。一个对于 多(multiple)或全部(all)索引的搜索的工作机制和这完全一致——仅仅是多了一些分片而已。

二、取回阶段:

查询阶段辨别出那些满足搜索请求的document,但我们仍然需要取回那些document本身。这就是取回阶段的工作,如图分布式搜索的取回阶段所示。

ES为什么不支持一次查询10000条以后的数据?_第2张图片

分发阶段由以下步骤构成:
1.协调节点辨别出哪个document需要取回,并且向相关分片发出 GET  请求。
2.每个分片加载document并且根据需要丰富(enrich)它们,然后再将document返回协调节点。
3.一旦所有的document都被取回,协调节点会将结果返回给客户端。
    协调节点先决定哪些document是实际(actually)需要取回的。例如,我们指定查询 { "from": 90, "size": 10 }  ,那么前90条将会被丢弃,只有之后的10条会需要取回。这些document可能来自与原始查询请求相关的某个、某些或者全部分片。
    协调节点为每个持有相关document的分片建立多点get请求然后发送请求到处理查询阶段的分片副本。
    分片加载document主体—— _source  field。如果需要,还会根据元数据丰富结果和高亮搜索片断。一旦协调节点收到所有结果,会将它们汇集到单一的回答响应里,这个响应将会返回给客户端。

 

但是很多时候10000数据不能满足项目的需求,可以通过以下两个方案解决。
 
解决方案一:
1、修改elasticsearch输出默认限制条数

 

PUT 索引名称/_settings?preserve_existing=true
{
  "max_result_window": "1000000"
}

2、设置后只对已经存在的索引生效,新建的索引需重新设置

3、设置时索引名称可以使用通配符进行设置

 

解决方案二:

创建索引时设置

"settings":{
    "index":{
        "max_result_window":1000000
   } 
}

 

 

深度分页

 查询然后取回过程虽然支持通过使用 from  和 size  参数进行分页,但是要在有限范围内(within limited)。还记得每个分片必须构造一个长度为 from+size  的优先队列吧,所有这些都要传回协调节点。这意味着协调节点要通过对 分片数量 * (from +size)个document进行排序来找到正确的 size  个document

    比如:根据document的数量,分片的数量以及所使用的硬件,对10,000到50,000条结果(1,000到5,000页)深分页是可行的。是对于足够大的 from  值,排序过程将会变得非常繁重,会使用巨大量的CPU,内存和带宽。因此,强烈不建议使用深分页。在实际中,“深分页者”也是很少的一部人。一般人会在翻了两三页后就停止翻页,并会更改搜索标准。那些不正常情况通是机器人或者网络爬虫的行为。它们会持续不断地一页接着一页地获取页面直到服务器到底崩溃的边缘。

注意事项

通过上述的方式解决了我们的问题,但也引入了另一个需要我们注意的问题,窗口值调大了后,虽然请求到分页的数据条数更多了,但它是用牺牲更多的服务器的内存、CPU资源来换取的。要考虑业务场景中过大的分页请求,是否会造成集群服务的OutOfMemory问题。在ES的官方文档中对深度分页也做了讨论

https://www.elastic.co/guide/en/elasticsearch/guide/current/pagination.html

https://www.elastic.co/guide/en/elasticsearch/guide/current/pagination.html

 

posted @ 2020-10-21 09:50  RainSail  阅读(9368)  评论(0)    收藏  举报