对ES官网的reference的翻译,同时也是备忘,ES版本为7.5

下面是正文翻译,附上原文链接:

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-percentile-aggregation.html

==================================================================================================

百分位数聚合

多值指标聚合,计算从聚合的文档中提取的数值上的一个或多个百分位值。这些值可以从文档中某些特定的数值字段中提取出来,也可以使用给定的脚本生成。

百分位数表示的是一定百分比的观测值所出现的点。例如,第95个百分位数是比95%的观测值都要大的值。(摘自百度百科:一组n个观测值按数值大小排列。如,处于p%位置的值称第p百分位数)。

百分位数常被用来找出离群值。在正态分布中,第0.13个百分位数和第99.87个百分位数代表从均值偏移三个标准差的值,任何落在从均值偏移三个标准差之外的数据都被认为是异常值。

当提取了一定范围内的百分位数时,它们能被用来估计数据的分布并确定数据是否倾斜、双峰等。

假设你的数据有网站载入时间组成,对于管理员来说,平均载入时间和中位数载入时间并不太有用。最大载入时间可能令人感兴趣,但这个值很容易被单个慢响应所倾斜。

让我们看一下表示载入时间的某些百分位数(load_time一定要是数值字段):

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "load_time_outlier": {
            "percentiles": {
                "field": "load_time"
            }
        }
    }
}'

默认的,percentiles指标会生成[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
         }
      }
   }
}

你可以看到,聚合会为默认范围内的每个百分位数返回一个计算的值。我们假设响应时间的单位是ms,显然网页一般是在10-725ms内载入,但也会偶尔飙升到945-985ms。

管理员通常只对离群值(极端的百分位数)感兴趣,我们可以只指定我们感兴趣的百分位数(要求的百分位数必须是介于0到100之间的一个值,使用percents参数制定需要计算的特殊的百分位数):

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "load_time_outlier": {
            "percentiles": {
                "field": "load_time",
                "percents": [95,99,99.9]
            }
        }
    }
}'

Keyed响应

 默认的,keyed标志被设置为true,在这种情况下,每个bucket会跟一个独特的字符串key关联并将范围作为hash而不是array返回。将keyed标志位设置为false会禁止上面的行为:

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "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
                }
            ]
        }
    }
}

脚本

 百分位数指标支持使用脚本。例如,如果我们的载入时间的单位是ms但我们想要百分位数的计算单位变成s,我们可以使用脚本来进行时间的转换:

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "load_time_outlier": {
            "percentiles": {
                "script": {
                    "lang": "painless",
                    "source": "doc['load_time'].value/params.timeUnit",
                    "params": {
                        "timeUnit": 1000    
                    }
                }
            }
        }
    }
}'

与之前的请求体相比,filed参数被script参数替代,script参数使用脚本生成被计算百分位数的观测值,与其他脚本类似,我们也可以给脚本中传入参数。

上面的请求使用的是painless脚本语言编写的无参数的内联脚本,为了使用缓存的脚本,使用下面的请求:

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "load_time_outlier": {
            "percentiles": {
                "script": {
                    "id": "my_script",
                    "params": {
                        "field": "load_time"    
                    }
                }
            }
        }
    }
}'

百分位数(通常)是近似的

有许多不同的算法来计算百分位数。 最原始的实现就是简单的将所有的观测值都存在排序好的数组中,要找到第50个百分位数,你只需要找到位于my_array[count(my_array)*0.5]位置的数值即可。

显然,上面提到的原始实现方式是不收敛的——排好序的数组长度与数据集中的数据量个数成正比。为了在一个ES集群中计算量级可能达到数十亿的数据的百分位数,我们实际计算的是近似的百分位数。

percentiles指标使用的算法叫做TDigest(由Ted Dunning引进)。

当使用percentiles指标是,我们需要记住下面的这几点准则:

1、准确率与q(1-q)成比例,这意味着极端的百分位数(比如99%)比不极端的百分位数(比如中位数)更精确

2、对于小集合来说,百分位数高度准确(如果数据集足够小,可能达到100%的准确率)

3、随着bucket中数值的量级增大,算法开始逼近百分位数,该算法高效地牺牲准确率而节省内存。不准确率的精确值很难泛化,因为 准确率依赖于数据分布以及聚合的数据的体量。

下面的图表显示了在正态分布的数据集上的相对误差随着数据集大小以及请求的百分位数的变化情况:

es multi_match参数 es ram.percent_直方图

可以看到在极端情况的百分位数下,准确率更高(可以看到数据集大小取1000-10000之间的某个值时,第50个百分位数的误差非常高,其次是第90个百分位数,然后是第99个百分位数,误差最低的是第99.9个百分位数。但是在同样的百分位数情况下,误差并不一定会随着数据集个数的增加而升高)。在更大的百分位数情况下误差降低的原因在于大数定律让观测值的分布更加均匀,而percentile指标用到的t-digest树能够更好的总结这些数据。但在更倾斜的分布情况下,这一点将不再成立。

NOTE:百分位数聚合是不确定的,这意味着在相同数据集上进行多次百分位数聚合可能得到稍微不一样的结果。

压缩

近似算法必须要在内存使用以及近似准确性之间平衡,这种平衡可以通过compression参数来控制(compression控制的是内存使用和近似误差):

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "load_time_outlier": {
            "percentiles": {
                "field": "load_time",
                "tdigest": {
                    "compression": 200
                }
            }
        }
    }
}'

TDigest算法使用一定数量的"节点"来近似计算百分位数——可用“节点”越多,与数据量成正比的精度(和较大的内存占用量)就越高,compression参数限制节点的数目为20*compression。

因此,通过增大compression的值,我们可以消耗更多的内存来提高百分位数的准确性,当然更大的compression值也会让算法变慢,因为算法底层的tree数据结构的size增长,导致代价更加昂贵的操作。默认的compression值为100.

单个“节点”使用大约32byte内存,所以在最差情况下(大量数据按顺序到达)默认的设置会产生大约64KB的TDigest,实际上数据可能是更加随机的,这种情况下TDigest会使用更少的内存。

HDR直方图

NOTE:这个设置会把HDR直方图的内部实现暴露出来,未来的语法可能会有变化。

HDR直方图(高动态范围直方图)是一种可选的实现,该实现在计算延时测量的百分位数的情况下很有用,因为计算HDR直方图会比tdigest算法要快。HDR直方图实现维护一个固定的最坏情况百分比错误(由有效数字的个数指定)。举个例子,假如数据集包含从1微秒到1小时(3600000000微秒)的数据,HDR直方图被设置为3个有效数字,那么1毫秒的数据会维持1微秒的值分辨率,1小时的数据会维持3.6秒(或者更好)的值分辨率(3个有效数字意味着0.001的百分比错误)。

可以通过在请求中指定method参数使用HDR直方图:

curl http://host_ip:host_port/latency/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "load_time_outlier": {
            "percentiles": {
                "field": "load_time",
                "percents": [95,99,99.9],
                "hdr": {
                    "number_of_significant_value_digits": 3 
                }
            }
        }
    }
}'

hdr参数表明HDR直方图应该被用来计算百分位数,而与HDR直方图算法相关的具体设置可以在hdr中指定。

number_of_significant_value_digits参数以有效数字个数的方式制定了直方图中的值分辨率。

HDR直方图只支持正数,如果传给它负数会报错。在不知道数据集中数值的范围的情况下使用HDR直方图也不是一个好主意,因为这可能导致很高的内存使用。

缺失的值

missing这个参数定义了缺失某个字段的文档应该如何被处理,默认的这些文档会被忽略,但我们也能通过假设它们这个字段有值来处理这些文档。

curl http://host_ip:host_port/grade/_search?pretty
-H 'content-type:application/json'
-d '{
    "size": 0,
    "aggs": {
        "grade_percentiles": {
            "percentiles": {
                "field": "grade",
                "missing": 10
            }
        }
    }
}'

grade字段没有值的文档会落入与grade=10的文档相同的桶中。