ElasticSearch 在对海量数据进行聚合分析的时候会损失搜索的精准度来满足实时性的需求。

ES 聚合分析不精准原因分析-LMLPHPTerms聚合分析的执行流程:

ES 聚合分析不精准原因分析-LMLPHP

不精准的原因:

数据分散到多个分片,聚合是每个分片的取 Top X,导致结果不精准。ES 可以不每个分片Top X,而是全量聚合,但势必这会有很大的性能问题。

如何提高聚合精确度

方案1:设置主分片为1

注意7.x版本已经默认为1。

适用场景:数据量小的小集群规模业务场景。

方案2:调大 shard_size 值

设置 shard_size 为比较大的值,官方推荐:size * 1.5 + 10 。shard_size 值越大,结果越趋近于精准聚合结果值。此外,还可以通过 show_term_doc_count_error 参数显示最差情况下的错误值,用于辅助确定 shard_size 大小。

  • size:是聚合结果的返回值,客户期望返回聚合排名前三,size值就是 3。
  • shard_size: 每个分片上聚合的数据条数。shard_size 原则上要大于等于 size

适用场景:数据量大、分片数多的集群业务场景。

使用 kibana 样例数据进行测试

ES 聚合分析不精准原因分析-LMLPHP

采用索引迁移方式,将数据迁移到 我们自己创建的索引中

PUT my_flights
{
  "settings": {
    "number_of_shards": 20
  },
  "mappings" : {
      "properties" : {
        "AvgTicketPrice" : {
          "type" : "float"
        },
        "Cancelled" : {
          "type" : "boolean"
        },
        "Carrier" : {
          "type" : "keyword"
        },
        "Dest" : {
          "type" : "keyword"
        },
        "DestAirportID" : {
          "type" : "keyword"
        },
        "DestCityName" : {
          "type" : "keyword"
        },
        "DestCountry" : {
          "type" : "keyword"
        },
        "DestLocation" : {
          "type" : "geo_point"
        },
        "DestRegion" : {
          "type" : "keyword"
        },
        "DestWeather" : {
          "type" : "keyword"
        },
        "DistanceKilometers" : {
          "type" : "float"
        },
        "DistanceMiles" : {
          "type" : "float"
        },
        "FlightDelay" : {
          "type" : "boolean"
        },
        "FlightDelayMin" : {
          "type" : "integer"
        },
        "FlightDelayType" : {
          "type" : "keyword"
        },
        "FlightNum" : {
          "type" : "keyword"
        },
        "FlightTimeHour" : {
          "type" : "keyword"
        },
        "FlightTimeMin" : {
          "type" : "float"
        },
        "Origin" : {
          "type" : "keyword"
        },
        "OriginAirportID" : {
          "type" : "keyword"
        },
        "OriginCityName" : {
          "type" : "keyword"
        },
        "OriginCountry" : {
          "type" : "keyword"
        },
        "OriginLocation" : {
          "type" : "geo_point"
        },
        "OriginRegion" : {
          "type" : "keyword"
        },
        "OriginWeather" : {
          "type" : "keyword"
        },
        "dayOfWeek" : {
          "type" : "integer"
        },
        "timestamp" : {
          "type" : "date"
        }
      }
    }
}

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

迁移完成后,可以看到有一万多条数据 ,并且分片数量是 20

ES 聚合分析不精准原因分析-LMLPHP

而源索引的分片数量是 1

ES 聚合分析不精准原因分析-LMLPHP

然后采用同样的聚合查询:

先看一下默认分片为 1 的源索引:

POST /kibana_sample_data_flights/_search
{
  "size": 0, 
  "aggs": {
    "weather": {
      "terms": {
        "field": "OriginWeather",
        "size": 5,
        "show_term_doc_count_error": true
      }
    }
  }
}

这里有个属性说明一下:

show_term_doc_count_error:true

在Terms Aggregation的返回中有两个特殊的数值:

  • doc_count_error_upper_bound : 被遗漏的term 分桶,包含的文档,有可能的最大值
  • sum_other_doc_count: 除了返回结果 bucket 的 terms 以外,其他 terms 的文档总数(总数 - 返回的总数)

ES 聚合分析不精准原因分析-LMLPHP

 然后再看下 我们自己创建的索引 分片为 5 的情况

POST /my_flights/_search
{
  "size": 0, 
  "aggs": {
    "weather": {
      "terms": {
        "field": "OriginWeather",
        "size": 5,
        "shard_size": 5, 
        "show_term_doc_count_error": true
      }
    }
  }
}

可以看到此时就有可能被遗漏的文档了,因为我们设置的分片数是 20,此时查询只查询了 5 分片 

ES 聚合分析不精准原因分析-LMLPHP

把分片调大一些再看:

就没有可能被遗漏的数据了,说明调大分片数是可以增加聚合精度的,但是对性能肯定会有一定影响的

ES 聚合分析不精准原因分析-LMLPHP 适用场景:数据量大、分片数多的集群业务场景。

方案3:将size设置为全量值,来解决精度问题

将size设置为2的32次方减去1也就是分片支持的最大值,来解决精度问题。

原因:1.x版本,size等于 0 代表全部,高版本取消 0 值,所以设置了最大值(大于业务的全量值)。

全量带来的弊端就是:如果分片数据量极大,这样做会耗费巨大的CPU 资源来排序,而且可能会阻塞网络。

适用场景:对聚合精准度要求极高的业务场景,由于性能问题,不推荐使用。

方案4:使用 Clickhouse / Spark 进行精准聚合

适用场景:数据量非常大、聚合精度要求高、响应速度快的业务场景。

 感谢观看!!!感兴趣的小伙伴可以关注收藏,持续更新中~~~

03-16 13:37