Skip to main content

基数聚合 cardinality

Cardinality Aggregation

计算不同值的近似计数的单值度量聚合。值可以从文档中的特定字段提取,也可以由脚本生成。

假设您正在为商店销售编制索引,并希望统计与查询匹配的已售出产品的唯一数量:

POST /sales/_search?size=0
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "type"
}
}
}
}

返回结果

{
...
"aggregations" : {
"type_count" : {
"value" : 3
}
}
}

精度控制

POST /sales/_search?size=0
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "_doc",
"precision_threshold": 100
}
}
}
}

precision_threshold选项允许以内存换取精度,并定义一个唯一的计数,低于该计数的计数将接近精度。高于此值,计数可能会变得更加模糊。支持的最大值为40000,高于此值的阈值将与40000的阈值具有相同的效果。默认值为3000。

近似值计数

计算精确计数需要将值加载到哈希集并返回其大小。当处理高基数集和/或大值时,这不会扩展,因为所需的内存使用量和节点之间每个shard集的通信需要会占用集群的太多资源。

这种基数聚合基于HyperLogLog++算法,该算法基于具有一些有趣属性的值的哈希值进行计数:

  • 可配置精度,其决定如何以内存换取精度,
  • 在低基数集合上具有优异的精度,
  • 固定内存使用量:无论是否存在数百或数十亿个唯一值,内存使用量仅取决于配置的精度。

对于精度阈值c,我们使用的实现需要大约c*8字节。

下表显示了阈值前后的误差变化情况

对于所有3个阈值,计数都精确到配置的阈值。虽然不能保证,但情况很可能是这样。实践中的准确性取决于所讨论的数据集。一般来说,大多数数据集显示出一致的良好准确性。还要注意的是,即使阈值低至100,即使在计算数百万项时,误差仍然很低(如上图所示,为1-6%)。

HyperLogLog++算法依赖于哈希值的前导零,数据集中哈希值的精确分布会影响基数的准确性。

还请注意,即使阈值低至100,错误仍然很低,即使在计算数百万项时也是如此。

预计算 hash

对于具有高基数的字符串字段,将字段值的哈希存储在索引中,然后对该字段运行基数聚合可能会更快。这可以通过从客户端提供哈希值来实现,也可以通过使用mapper-murmur3插件让Elasticsearch为您计算哈希值。

预计算散列通常只在非常大和/或高基数字段上有用,因为它节省了CPU和内存。然而,在数字字段上,哈希运算非常快,存储原始值所需的内存与存储哈希值所需内存相同或更少。对于低基数字符串字段也是如此,特别是考虑到这些字段进行了优化,以确保每个段的每个唯一值最多计算一次散列

Script

cardinality 度量支持脚本编写,但由于需要实时计算哈希值,因此性能会受到显著影响。

POST /sales/_search?size=0
{
"aggs" : {
"type_promoted_count" : {
"cardinality" : {
"script": {
"lang": "painless",
"source": "doc['type'].value + ' ' + doc['promoted'].value"
}
}
}
}
}

这将把脚本参数解释为具有painless语言且没有脚本参数的inline script。要使用存储的脚本,请使用以下语法:

POST /sales/_search?size=0
{
"aggs" : {
"type_promoted_count" : {
"cardinality" : {
"script" : {
"id": "my_script",
"params": {
"type_field": "_doc",
"promoted_field": "promoted"
}
}
}
}
}
}

Missing value

缺少的参数定义了应如何处理缺少值的文档。默认情况下,它们将被忽略,但也可以将它们视为具有值。

POST /sales/_search?size=0
{
"aggs" : {
"tag_cardinality" : {
"cardinality" : {
"field" : "tag",
"missing": "N/A"
}
}
}
}

标记字段中没有值的文档将与值为N/a的文档属于同一存储桶。