Percentiles Aggregation
一种多值度量标准聚合,用于计算从聚合文档中提取的数值的一个或多个百分位数。可以从文档中的特定数字字段提取这些值,也可以通过提供的脚本生成这些值。
百分位数显示出一定百分比的观察值出现的点。例如,第95百分位数是大于观察值的95%的值。
百分位数通常用于查找异常值。在正态分布中,第0.13和99.87百分位表示与平均值的三个标准偏差。任何超出三个标准偏差的数据通常被认为是异常。
当检索到一系列百分位数时,它们可用于估计数据分布并确定数据是否偏斜,双峰等。
假设您的数据包含网站加载时间。平均和中位加载时间对管理员来说并不过分有用。最大值可能很有趣,但它可以很容易地被一个慢响应扭曲。
让我们看一下表示加载时间的百分位数范围:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time" # @1
}
}
}
}
@1: 字段load_time必须是数字字段
默认情况下,百分位度量将生成百分位数范围:[1,5,25,50,75,95,99]。响应将如下所示:
{
...
"aggregations": {
"load_time_outlier": {
"values" : {
"1.0": 5.0,
"5.0": 25.0,
"25.0": 165.0,
"50.0": 445.0,
"75.0": 725.0,
"95.0": 945.0,
"99.0": 985.0
}
}
}
}
如您所见,聚合将返回默认范围内每个百分位数的计算值。如果我们假设响应时间以毫秒为单位,则很明显网页通常在10-725ms内加载,但偶尔会出现945-985ms的峰值。
通常,管理员只对异常值感兴趣 - 极端百分位数。我们可以只指定我们感兴趣的百分比(请求的百分位数必须是0到100之间的值):
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"percents" : [95, 99, 99.9] # @1
}
}
}
}
@1: 使用百分比参数指定要计算的特定百分位数
Keyed Response
默认情况下,keyed标志设置为true,它将唯一的字符串键与每个存储桶相关联,并将范围作为哈希而不是数组返回。将keyed标志设置为false将禁用此行为:
GET latency/_search
{
"size": 0,
"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "load_time",
"keyed": false
}
}
}
}
响应:
{
...
"aggregations": {
"load_time_outlier": {
"values": [
{
"key": 1.0,
"value": 5.0
},
{
"key": 5.0,
"value": 25.0
},
{
"key": 25.0,
"value": 165.0
},
{
"key": 50.0,
"value": 445.0
},
{
"key": 75.0,
"value": 725.0
},
{
"key": 95.0,
"value": 945.0
},
{
"key": 99.0,
"value": 985.0
}
]
}
}
}
Script
百分位度量标准支持脚本。例如,如果我们的加载时间以毫秒为单位但我们想要以秒为单位计算百分位数,我们可以使用脚本即时转换它们:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"script" : {
"lang": "painless",
"source": "doc['load_time'].value / params.timeUnit", # @1
"params" : {
"timeUnit" : 1000 #@2
}
}
}
}
}
}
@1: field参数替换为script参数,该参数使用脚本生成计算百分位数的值
@2: Scripting支持参数化输入,就像任何其他脚本一样
这将使用无痛脚本语言并且没有脚本参数将脚本参数解释为内联脚本。要使用存储的脚本,请使用以下语法:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"script" : {
"id": "my_script",
"params": {
"field": "load_time"
}
}
}
}
}
}
Percentiles are (usually) approximate 百分位数(通常)是近似值
有许多不同的算法来计算百分位数。朴素的实现只是将所有值存储在已排序的数组中。要找到第50个百分位数,只需找到my_array中的值[count(my_array)* 0.5]。
显然,天真的实现不会扩展 - 排序的数组会随着数据集中的值的数量线性增长。要计算Elasticsearch集群中潜在数十亿个值的百分位数,请计算近似百分位数。
百分位度量使用的算法称为TDigest(由Ted Dunning在使用T-Digests计算精确分位数时引入)。
使用此指标时,请注意以下几条准则:
- 精度与q(1-q)成正比。这意味着极端百分位数(例如99%)比不太极端的百分位数(例如中位数)更准确
- 对于较小的值集,百分位数非常准确(如果数据足够小,则可能100%准确)。
- 随着桶中值的数量增加,算法开始接近百分位数。它有效地节省了内存的交易准确性。确切的不准确程度很难概括,因为它取决于您的数据分布和聚合的数据量
下图显示了均匀分布的相对误差,具体取决于所收集的值的数量和请求的百分位数:
它显示了极端百分位数的精度更好。对于大量值,误差减小的原因在于,大数定律使得值的分布越来越均匀,并且t-digest树在总结它时可以做得更好。对于更偏斜的分布,情况并非如此。
Compression 压缩
近似算法必须平衡内存利用率和估计准确性。可以使用压缩参数控制此平衡:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"tdigest": {
"compression" : 200 # @1
}
}
}
}
}
@1: 压缩控制内存使用和逼近错误
TDigest算法使用许多“节点”来估计百分位数 - 可用节点越多,与数据量成比例的准确度(和大内存占用量)就越高。压缩参数将最大节点数限制为20 *压缩。
因此,通过增加压缩值,您可以以更多内存为代价提高百分位数的准确性。较大的压缩值也会使算法变慢,因为底层树数据结构的大小会增加,从而导致更昂贵的操作。默认压缩值为100。
“节点”使用大约32个字节的内存,因此在最坏情况下(大量数据到达排序和按顺序),默认设置将产生大约64KB的TDigest。在实践中,数据往往更随机,TDigest将使用更少的内存。
HDR Histogram
NOTE 此设置公开HDR直方图的内部实现,语法可能在将来发生变化。
HDR直方图(高动态范围直方图)是一种替代实现,在计算延迟测量的百分位数时非常有用,因为它可以比t-digest实现更快,并且需要更大的内存占用。此实现维护固定的最坏情况百分比错误(指定为有效位数)。这意味着如果数据以1微秒到1小时(3,600,000,000微秒)的值记录在直方图中设置为3位有效数字,则对于高达1毫秒和3.6秒(或更高)的值,它将保持1微秒的值分辨率)最大跟踪值(1小时)。
可以通过在请求中指定方法参数来使用HDR直方图:
GET latency/_search
{
"size": 0,
"aggs" : {
"load_time_outlier" : {
"percentiles" : {
"field" : "load_time",
"percents" : [95, 99, 99.9],
"hdr": { # @1
"number_of_significant_value_digits" : 3 # @2
}
}
}
}
}
@1: hdr对象表示应使用HDR直方图来计算百分位数,并且可以在对象内指定此算法的特定设置
@2: number_of_significant_value_digits以有效位数指定直方图的值的分辨率
HDRHistogram仅支持正值,如果传递负值,则会出错。如果值的范围未知,则使用HDRHistogram也不是一个好主意,因为这可能导致高内存使用。
Missing value
缺少的参数定义了应该如何处理缺少值的文档。默认情况下,它们将被忽略,但也可以将它们视为具有值。
GET latency/_search
{
"size": 0,
"aggs" : {
"grade_percentiles" : {
"percentiles" : {
"field" : "grade",
"missing": 10 # @1
}
}
}
}
@1: 在成绩字段中没有值的文档将与具有值10的文档属于同一个存储桶。