ES基于分布式,每一个搜索请求都是分发到所有的分片上单独处理,最后汇总结果。聚合也是这样操作。但是在这种逻辑下,会导致某些聚合的结果不准确,即实现group逻辑的terms桶

curl -X GET "localhost:9200/cars/transactions/_search" -H 'Content-Type: application/json' -d'
{
    "size" : 0,
    "aggs" : { 
        "popular_colors" : { 
            "terms" : { 
              "field" : "color",
              "size": 3
            }
        }
    }
}

这里实现了一个terms聚合,以color为依据进行分组统计。结果为每种color的个数。下面是返回的结果

{
...
   "hits": {
      "hits": [] 
   },
   "aggregations": {
      "doc_count_error_upper_bound": 3,
      "sum_other_doc_count": 10,
      "popular_colors":
         "buckets": [
            {
               "key": "red", 
               "doc_count": 4 
            },
            {
               "key": "blue",
               "doc_count": 2
            },
            {
               "key": "green",
               "doc_count": 2
            }
         ]
      }
   }
}

在返回结果的aggregations中,有三个值:doc_count_error_upper_bound、sum_other_doc_count和popular_colors。这三个值表示了ES对这次聚合的评估和返回的聚合结果。

doc_count_error_upper_bound:表示没有在这次聚合中返回、但是可能存在的潜在聚合结果,这里的值为 3,表除去red、blue和green之外,还可能有一个聚合结果为 3。从返回结果看,可能会排在第二名。

sum_other_doc_count:表示这次聚合中没有统计到的文档数。因为ES为分布式部署,不同文档分散于多个分片,这样当聚合时,会在每个分片上分别聚合,然后由协调节点汇总结果后返回。这里因为请求的是排名前三的聚合,所以每个分片只聚合了本分片中排名前三的color。其他没有统计到的文档数由每个节点返回后,汇总为此元素。这里值为 10,表示有10个文档没有参与此次聚合。

popular_colors:聚合结果,默认由高到低排列。key表示聚合元素的值,doc_count表示元素出现的次数。注意,这里的doc_count也是不准确的。

由此分析这一次的聚合,虽然返回了top3,但是可能存在另一个第二名,而且doc_count只是一个相对值。
本质上这种误差是由于分布式环境中的数据隔离产生的,尤其是当获取topK数据时,节点内的topK准确不保证整体topK准确。而且无论元素还是元素对应的doc_count,都不是准确的。
所以如果想要ES获取准确的聚合结果,只有max、min、avg等聚合可以做到,而terms API本身不保证准确,只有通过一些额外的方法来确保或者提高准确性:

  1. 不分片,所有数据在一个shard上。这样可以使ES在聚合中使用所有数据,为完全准确。
  2. 在聚合中使用route,将需要聚合的数据路由到同一个节点上。将这样需要数据结构和业务逻辑的支持,一般数据很难实现。
  3. 提高聚合结果的数量。这样可以使每一个分片节点返回更多的冗余数据,在协调节点进行数据汇总时,提高聚合结果的准确性。这种方法只保证聚合结果的近似准确,推荐使用。
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐