百分位聚合

百分位聚合 #

percentiles 百分位聚合估计数值字段在给定百分位处的值。这对于理解分布边界很有用。

例如, load_time 的 95th 百分位 = 120ms 表示 95% 的值小于或等于 120 毫秒。

cardinality 指标类似, percentile 指标也是近似的。

参数说明 #

percentiles 聚合采用以下参数。

参数必需/可选数据类型描述
field必需String用于计算百分位数的数值字段。
percents可选Array of doubles返回百分位数列表。默认为 [1, 5, 25, 50, 75, 95, 99] 。
keyed可选Boolean如果设置为 false ,则将结果作为数组返回。否则将结果作为 JSON 对象返回。默认值为 true 。
tdigest.compression可选Double控制 tdigest 算法的准确性和内存使用。参见使用 tdigest 进行精度调整。
hdr.number_of_significant_value_digits可选IntegerHDR 直方图的精度设置。参见 HDR 直方图。
missing可选Numeric当文档中目标字段缺失时使用的默认值。
script可选Object用于计算自定义值而不是使用字段的脚本。支持内联和存储脚本。

参考样例 #

首先,创建一个索引:

PUT /latency_data
{
  "mappings": {
    "properties": {
      "load_time": {
        "type": "double"
      }
    }
  }
}

添加示例数值以说明百分位数计算:

POST /latency_data/_bulk
{ "index": {} }
{ "load_time": 20 }
{ "index": {} }
{ "load_time": 40 }
{ "index": {} }
{ "load_time": 60 }
{ "index": {} }
{ "load_time": 80 }
{ "index": {} }
{ "load_time": 100 }
{ "index": {} }
{ "load_time": 120 }
{ "index": {} }
{ "load_time": 140 }

百分位数聚合 #

以下示例计算 load_time 字段的默认百分位数集:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time"
      }
    }
  }
}

默认情况下,会返回第 1、5、25、50、75、95 和 99 个百分位数:

{
  ...
  "aggregations": {
    "load_time_percentiles": {
      "values": {
        "1.0": 20,
        "5.0": 20,
        "25.0": 40,
        "50.0": 80,
        "75.0": 120,
        "95.0": 140,
        "99.0": 140
      }
    }
  }
}

自定义分位数 #

您可以使用 percents 数组指定确切的百分位数:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "percents": [50, 90, 99]
      }
    }
  }
}

返回内容仅包含所请求的三个百分位聚合:

{
  ...
  "aggregations": {
    "load_time_percentiles": {
      "values": {
        "50.0": 80,
        "90.0": 140,
        "99.0": 140
      }
    }
  }
}

键值响应 #

可以通过将 keyed 参数设置为 false 将返回的聚合格式从 JSON 对象更改为键值对列表:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "keyed": false
      }
    }
  }
}

响应以值数组的形式提供百分位数:

{
  ...
  "aggregations": {
    "load_time_percentiles": {
      "values": [
        {
          "key": 1,
          "value": 20
        },
        {
          "key": 5,
          "value": 20
        },
        {
          "key": 25,
          "value": 40
        },
        {
          "key": 50,
          "value": 80
        },
        {
          "key": 75,
          "value": 120
        },
        {
          "key": 95,
          "value": 140
        },
        {
          "key": 99,
          "value": 140
        }
      ]
    }
  }
}

使用 tdigest 进行精确度调整 #

tdigest 算法是计算百分位数的默认方法。它提供了一种内存高效的方式来估计百分位数排名,尤其是在处理浮点数据(如响应时间或延迟)时。

与精确的百分位数计算不同, tdigest 使用概率方法将值分组到质心——即总结分布的小型簇。这种方法能够在无需将所有原始数据存储在内存中的情况下,为大多数百分位数提供准确的估计。

该算法设计为在分布的尾部附近(即低百分位数(如第 1 位)和高百分位数(如第 99 位))具有高度准确性,这些通常是性能分析中最重要的一部分。您可以使用 compression 参数控制结果的精度。

较高的 compression 值意味着使用更多的质心,这会增加准确性(尤其是在尾部),但需要更多的内存和 CPU。较低的 compression 值会减少内存使用并加快执行速度,但结果可能不够准确。

适合使用 tdigest 的情况:

  • 您的数据包含浮点值,例如响应时间、延迟或持续时间。
  • 您需要在极端百分位数中获得准确的结果,例如第 1 位或第 99 位。

避免使用 tdigest 的情况:

  • 您只处理整数数据,并希望获得最大速度。
  • 您不太在意分布尾部的准确性,更倾向于快速聚合(可以考虑使用 hdr 代替)。

以下示例将 tdigest.compression 设置为 200 :

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "tdigest": {
          "compression": 200
        }
      }
    }
  }
}

HDR 直方图 #

高动态范围(HDR)直方图是计算百分位的另一种方法。它特别适用于处理大型数据集和延迟测量。它专为速度而设计,同时支持宽动态范围值,并保持固定且可配置的精度水平。

tdigest 不同,后者在分布的尾部(极端百分位)提供更高的准确性,HDR 优先考虑速度和范围内均匀的准确性。当分组的数量很大且不需要对稀有值进行极端精确度时,它效果最佳。

例如,如果你正在测量从 1 微秒到 1 小时的范围内的响应时间,并使用 3 位有效数字配置 HDR,它将记录值精度为 ±1 微秒(直到 1 毫秒)和 ±3.6 秒(接近 1 小时)。

这种权衡使得 HDR 比 tdigest 快得多,但内存消耗更大。

下表展示了 HDR 显著数字的分解情况。

有效数字相对精度(最大误差)
11 份在 10 份中 = 10%
21 份在 100 份中 = 1%
31 份在 1000 中 = 0.1%
41 份在 10000 中 = 0.01%
51 份在 100000 中 = 0.001%

如果您需要使用 HDR,则应:

  • 正在跨多个分组进行聚合。
  • 不需要在尾部的百分位数上要求极端的精确度。
  • 确保有足够的内存可用。

你应该避免 HDR,如果:

  • 尾部精度很重要。
  • 你正在分析偏斜或稀疏的数据分布。

以下示例中将 hdr.number_of_significant_value_digits 设置为 3 :

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "hdr": {
          "number_of_significant_value_digits": 3
        }
      }
    }
  }
}

缺省值 #

使用 missing 设置为不包含目标字段的文档配置备用值:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "load_time_percentiles": {
      "percentiles": {
        "field": "load_time",
        "missing": 0
      }
    }
  }
}

Script 脚本 #

可以使用脚本动态计算值,而不是指定字段。当您需要应用转换(如货币转换或应用权重)时,这很有用。

内联脚本 #

使用脚本计算派生值:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "adjusted_percentiles": {
      "percentiles": {
        "script": {
          "source": "doc['load_time'].value * 1.2"
        },
        "percents": [50, 95]
      }
    }
  }
}

存储脚本 #

首先,使用以下请求创建一个示例脚本:

POST _scripts/load_script
{
  "script": {
    "lang": "painless",
    "source": "doc[params.field].value * params.multiplier"
  }
}

然后用 percentiles 聚合中的存储脚本,提供 params 存储脚本所需的:

GET /latency_data/_search
{
  "size": 0,
  "aggs": {
    "adjusted_percentiles": {
      "percentiles": {
        "script": {
          "id": "load_script",
          "params": {
            "field": "load_time",
            "multiplier": 1.2
          }
        },
        "percents": [50, 95]
      }
    }
  }
}