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本身不保证准确,只有通过一些额外的方法来确保或者提高准确性:
- 不分片,所有数据在一个shard上。这样可以使ES在聚合中使用所有数据,为完全准确。
- 在聚合中使用route,将需要聚合的数据路由到同一个节点上。将这样需要数据结构和业务逻辑的支持,一般数据很难实现。
- 提高聚合结果的数量。这样可以使每一个分片节点返回更多的冗余数据,在协调节点进行数据汇总时,提高聚合结果的准确性。这种方法只保证聚合结果的近似准确,推荐使用。