ES系列之聚合统计不准确问题对应解决方案

本篇文章不是讲ElasticSearch(下面简称ES)聚合分析的基本概念和用法的,这些网上的资料很多,不清楚的可以自行查阅。

我下面聚合分析使用的数据都是kibana自带的,这样方便有些读者实际测试文中的示例。

基本概念

ES为了满足搜索的实时性,在聚合分析的一些场景会通过损失精准度的方式加快结果的返回。这其实ES在实时性和精准度中间的权衡。

需要明确的是,并不是所有的聚合分析都会损失精准度,比如min,max等这些就没有精准度的问题。

可能这样直接说不好理解,下面会有详细的分析。

问题描述

我们通过一个示例引入问题。

首先我会把kibana自带的航班信息索引(名为kibana_sample_data_flights)reindex到我自定义的一个索引(名为my_flights)中,我的mapping和自带的索引完全一样,唯一的区别在于我设置了20个分片。索引的设置如下:

 
  1. PUT my_flights

  2. {

  3.   "settings": {

  4.     "number_of_shards": 20

  5.   },

  6.   "mappings" : {

  7.       "properties" : {

  8.         "AvgTicketPrice" : {

  9.           "type" : "float"

  10.         },

  11.         省略其它部分

reindex(以后有专门的文章讲reindex)的过程比较慢,我的电脑大概需要一分钟左右。

 
  1. POST _reindex

  2. {

  3.   "source": {

  4.     "index": "kibana_sample_data_flights"

  5.   },

  6.   "dest": {

  7.     "index": "my_flights"

  8.   }

  9. }

然后我们执行聚合分析的查询,这个查询是根据航班的目的地进行分桶。

 
  1. GET my_flights/_search

  2. {

  3.   "size": 0, 

  4.   "aggs": {

  5.     "dest": {

  6.       "terms": {

  7.         "field": "DestCountry"

  8.       }

  9.     }

  10.   }

  11. }

结果如下,

 
  1. {

  2.   "took" : 9,

  3.   "timed_out" : false,

  4.   "_shards" : {

  5.     "total" : 20,

  6.     "successful" : 20,

  7.     "skipped" : 0,

  8.     "failed" : 0

  9.   },

  10.   "hits" : {

  11.     "total" : {

  12.       "value" : 10000,

  13.       "relation" : "gte"

  14.     },

  15.     "max_score" : null,

  16.     "hits" : [ ]

  17.   },

  18.   "aggregations" : {

  19.     "dest" : {

  20.       "doc_count_error_upper_bound" : 52,

  21.       "sum_other_doc_count" : 3187,

  22.       "buckets" : [

  23.         {

  24.           "key" : "IT",

  25.           "doc_count" : 2371

  26.         },

  27.         {

  28.           "key" : "US",

  29.           "doc_count" : 1987

  30.         },

  31.         其它部分省略

在返回结果的aggregations中,有两个值:doc_count_error_upper_bound和sum_other_doc_count,我先来解释下,

  • doc_count_error_upper_bound:表示没有在这次聚合中返回,但是可能存在的潜在聚合结果。

  • sum_other_doc_count:表示这次聚合中没有统计到的文档数。这个好理解,因为ES统计的时候默认只会根据count显示排名前十的分桶。如果分类(这里是目的地)比较多,自然会有文档没有被统计到。

而这个doc_count_error_upper_bound就是我们本文要关注的重点对象,这个指标其实就是告诉用户本次的聚合结果究竟有多不精确。

问题分析

ES基于分布式,聚合分析的请求都是分发到所有的分片上单独处理,最后汇总结果。ES的terms聚合本身是前几个(size指定)结果,这就导致了结果必然有误差。

如上图所示,我们进行一个terms分桶查询,取前面3个结果。ES给出的结果是 A,B,C三个term,文档数量分别是12, 6, 4。

但是我们看最下面两个分片上的文档分布,人工也能看出来其实D应该是在结果中的,因为D的文档数量有6个,比C多,所以比较精确的结果应该是A,B,D。

产生问题的原因在于ES在对每个分片单独处理的时候,第一个分片的结果是A,B,C,第二个分片是A,B,D,并且第一个分片的C的文档数量大于D。所以汇总后的结果是A,B,C。

如何提高精准度

讨论完了问题,现在来看看如何解决问题。一般的方案有几种:

不分片

设置主分片为1,也就是不分片了。这个显而易见,上面分析聚合不精确的核心原因就在于分片,所以不分片肯定可以解决问题。但是缺点也是显然的,只适用于数据量小的情况下,如果数据量大都在一个分片上会影响ES的性能。

我们来做个测试,看看不分片的效果。我们使用自带的kibana_sample_data_flights索引来执行分桶聚合。

 
  1. GET kibana_sample_data_flights/_search

  2. {

  3.   "size": 0, 

  4.   "aggs": {

  5.     "dest": {

  6.       "terms": {

  7.         "field": "DestCountry"      

  8.         , "size": 3

  9.       }

  10.     }

  11.   }

  12. }

结果是,

 
  1. {

  2.   "took" : 2,

  3.   "timed_out" : false,

  4.   "_shards" : {

  5.     "total" : 1,

  6.     "successful" : 1,

  7.     "skipped" : 0,

  8.     "failed" : 0

  9.   },

  10.   "hits" : {

  11.     "total" : {

  12.       "value" : 10000,

  13.       "relation" : "gte"

  14.     },

  15.     "max_score" : null,

  16.     "hits" : [ ]

  17.   },

  18.   "aggregations" : {

  19.     "dest" : {

  20.       "doc_count_error_upper_bound" : 0,

  21.       "sum_other_doc_count" : 7605,

  22.       "buckets" : [

  23.         {

  24.           "key" : "IT",

  25.           "doc_count" : 2371

  26.         },

  27.         其它部分省略

因为kibana_sample_data_flights索引的分片数量是1,所以没有损失精准度。

提高聚合的数量

如下所示,把size设置成20(默认情况是10)聚合查询。size是指定聚合返回的结果数量。返回的结果越多,精确度肯定就越高。

 
  1. GET my_flights/_search

  2. {

  3.   "size": 0, 

  4.   "aggs": {

  5.     "dest": {

  6.       "terms": {

  7.         "field": "DestCountry"      

  8.         , "size": 20

  9.       }

  10.     }

  11.   }

  12. }

结果,

 
  1. "aggregations" : {

  2.     "dest" : {

  3.       "doc_count_error_upper_bound" : 0,

  4.       "sum_other_doc_count" : 571,

  5.       "buckets" : [

  6.         {

  7.           "key" : "IT",

  8.           "doc_count" : 2371

  9.         },

  10.         其它部分省略

结果也是没有精准度的损失了。

调大shard_size值

这个值表示要从分片上拿来计算的文档数量。默认情况下和size是一样的。取得size的值越大,结果会越接近准确,不过很明显会影响性能。

总结

  1. ES某些聚合统计会存在损失精准度的问题

  2. 损失精准度的原因是分片处理中间结果,汇总引起的误差,是ES实时性和精准度的权衡

  3. 可以通过调大shard_size等方法增加精准度 

转载:ES系列之原来ES的聚合统计不准确啊 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Elasticsearch 中,可以使用聚合(Aggregation)实现对文档进行聚合统计,其中包括出现次数的统计。下面是一个示例: 假设我们有一个名为 "sales" 的索引,包含以下文档: ``` { "product": "A", "price": 10.0, "timestamp": "2021-08-01T10:00:00Z" } { "product": "B", "price": 15.0, "timestamp": "2021-08-01T10:05:00Z" } { "product": "A", "price": 12.0, "timestamp": "2021-08-01T10:10:00Z" } { "product": "C", "price": 20.0, "timestamp": "2021-08-01T10:15:00Z" } { "product": "A", "price": 8.0, "timestamp": "2021-08-01T10:20:00Z" } { "product": "B", "price": 18.0, "timestamp": "2021-08-01T10:25:00Z" } ``` 现在,我们想要统计每个产品出现的次数,可以使用以下聚合查询: ``` { "aggs": { "products": { "terms": { "field": "product" } } } } ``` 其中,"aggs" 是聚合查询的关键字,"products" 是我们给这个聚合起的名字,"terms" 表示我们要按照某个字段进行分组,"field" 指定了我们要按照哪个字段进行分组。 运行上述查询后,得到的结果如下: ``` { "aggregations": { "products": { "buckets": [ { "key": "A", "doc_count": 3 }, { "key": "B", "doc_count": 2 }, { "key": "C", "doc_count": 1 } ] } } } ``` 其中,"key" 表示产品名称,"doc_count" 表示该产品出现的次数。 如果想要对出现次数进行排序,可以使用以下聚合查询: ``` { "aggs": { "products": { "terms": { "field": "product", "order": { "_count": "desc" } } } } } ``` 其中,"order" 表示按照什么字段进行排序,"_count" 表示按照出现次数进行排序,"desc" 表示降序排列。 运行上述查询后,得到的结果如下: ``` { "aggregations": { "products": { "buckets": [ { "key": "A", "doc_count": 3 }, { "key": "B", "doc_count": 2 }, { "key": "C", "doc_count": 1 } ] } } } ``` 其中,产品 A 出现的次数最多,排在第一位。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值