ES系列之原来ES的聚合统计不许确啊

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

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

基本概念

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

须要明确的是,并非全部的聚合分析都会损失精准度,好比min,max等这些就没有精准度的问题。测试

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

问题描述

咱们经过一个示例引入问题。code

首先我会把kibana自带的航班信息索引(名为kibana_sample_data_flights)reindex到我自定义的一个索引(名为my_flights)中,个人mapping和自带的索引彻底同样,惟一的区别在于我设置了20个分片。索引的设置以下:对象

PUT my_flights
{
  "settings": {
    "number_of_shards": 20
  },
  "mappings" : {
      "properties" : {
        "AvgTicketPrice" : {
          "type" : "float"
        },
        省略其它部分

reindex(之后有专门的文章讲reindex)的过程比较慢,个人电脑大概须要一分钟左右。blog

POST _reindex
{
  "source": {
    "index": "kibana_sample_data_flights"
  },
  "dest": {
    "index": "my_flights"
  }
}

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

GET my_flights/_search
{
  "size": 0, 
  "aggs": {
    "dest": {
      "terms": {
        "field": "DestCountry"
      }
    }
  }
}

结果以下,ip

{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 20,
    "successful" : 20,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "dest" : {
      "doc_count_error_upper_bound" : 52,
      "sum_other_doc_count" : 3187,
      "buckets" : [
        {
          "key" : "IT",
          "doc_count" : 2371
        },
        {
          "key" : "US",
          "doc_count" : 1987
        },
        
        其它部分省略

在返回结果的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指定)结果,这就致使告终果必然有偏差。

1-1.png

如上图所示,咱们进行一个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索引来执行分桶聚合。

GET kibana_sample_data_flights/_search
{
  "size": 0, 
  "aggs": {
    "dest": {
      "terms": {
        "field": "DestCountry"      
        , "size": 3
      }
    }
  }
}

结果是,

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "dest" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 7605,
      "buckets" : [
        {
          "key" : "IT",
          "doc_count" : 2371
        },
        其它部分省略

由于kibana_sample_data_flights索引的分片数量是1,因此没有损失精准度。

提升聚合的数量

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

GET my_flights/_search
{
  "size": 0, 
  "aggs": {
    "dest": {
      "terms": {
        "field": "DestCountry"      
        , "size": 20
      }
    }
  }
}

结果,

"aggregations" : {
    "dest" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 571,
      "buckets" : [
        {
          "key" : "IT",
          "doc_count" : 2371
        },
        其它部分省略

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

调大shard_size值

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

总结

  1. ES某些聚合统计会存在损失精准度的问题
  2. 损失精准度的缘由是分片处理中间结果,汇总引发的偏差,是ES实时性和精准度的权衡
  3. 能够经过调大shard_size等方法增长精准度

参考:

  • 极客时间《Elasticsearch核心技术与实战》
相关文章
相关标签/搜索