本文分享自华为云社区《Prometheus最佳实践 Summary和Histogram》,作者:张俭。
前言
Histogram 和 Summary都是复杂的指标,不仅仅是因为直方图和summary包含了多个时间序列,而且它们还较难使用正确。
观测中的Count和Sum
Histo和summary都是采样观测,典型的采样维度有 响应大小 和 请求时长 。它们跟踪观测值的数量和观测值的总和,从而使您可以计算观测值的平均值。 请注意,观察值的数量(在Prometheus中显示为带有" _count"后缀的时间序列)本质上是一个计数器(如上所述,它只会增加)。 观测值的总和(以带有_sum后缀的时间序列显示)也可以充当计数器,只要没有负面的观测值即可。 显然,请求持续时间或响应大小永远不会为负。 但是,原则上,您可以使用摘要和直方图来观察负值(例如,摄氏温度)。 在这种情况下,观察值的总和可能会下降,因此您无法再对其应用" rate()"。
例如,在Histo或者summary上计算5分钟内的平均请求时长,使用如下的PromQL
scss
rate(http_request_duration_seconds_sum[5m])/rate(http_request_duration_seconds_count[5m])
Apdex 分数
Histo(而不是summary)的一个直接用途是对落入指定观察值桶中的观察值进行计数。
你可能会有诸如这样的SLO:百分之95的请求都要在300ms内完成返回。在这个场景下,定义一个Histo,定义一个桶的界限为300ms。然后当这个桶的数量小于百分之95的时候就告警。
如下的表达式可以计算上文所述的SLO
scss
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)/sum(rate(http_request_duration_seconds_count[5m])) by (job)
你可以以一种非常相似的方式估计Apdex分数。以请求时长为例,配置上界为目标请求时长,另一个桶配置为最大忍受时长(通常是目标请求时长的四倍)。例如目标请求时长为300ms,最大忍受请求时长为1.2s。如下的表达式计算了每5分钟的Apdex分数
scss
(sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job) + sum(rate(http_request_duration_seconds_bucket{le="1.2"}[5m])) by (job) )/ 2 / sum(rate(http_request_duration_seconds_count[5m])) by (job)
注: Apdex分数计算
Apdex指数 =(1 × 满意样本 + 0.5 × 容忍样本)÷ 样本总数
请注意,我们将两个存储桶的总和相除。 原因是直方图存储桶是[累积](en.wikipedia.org/wiki/Histog... le =" 0.3"
存储桶也包含在le =" 1.2"
存储桶中; 将其除以2即可解决此问题。
该计算与传统的Apdex分数不完全匹配,因为它包括计算中满意和可忍受的部分中的误差。
Quantiles 分位数
你可以使用 summary 和 histogram 来计算 φ-分位数,φ在0到1之间,左右都为闭区间。φ分位数是一个用来计算前φ界限的观测值。换句话说,就是大家平时讲的pxx。p50即是中位数。为了打字方便,后面用 分位数 来行文。
Summary 和 Histogram 的一个区分要点是 summary 在客户端侧计算分位数,然后直接暴露它们,然而 histogram 在客户端侧暴露桶的技术,然后在服务端侧使用 histogram_quantitle() 函数来计算分位数。
请注意表中最后一项的重要性。 让我们回到在300毫秒内处理95%请求的SLO。 这次,您不想显示300毫秒内已处理请求的百分比,而是显示第95个百分位数,即您为95%的请求提供服务的请求持续时间。 为此,您可以配置 summary 为0.95位数,衰减时间为5分钟(例如),也可以配置直方图,并在300ms标记附近添加几个存储桶,例如 {le =" 0.1"}
,{le =" 0.2"}
,{le =" 0.3"}
和{le =" 0.45"}
。 如果您的服务使用多个实例进行复制运行,则将从其中的每个实例收集请求持续时间,然后将所有内容汇总到整体的95%。 但是,从 summary 汇总预先计算的分位数很少是有意义的。 在这种特定情况下,平均分位数会产生统计上无意义的值。
ini
avg(http_request_duration_seconds{quantile="0.95"}) // BAD!
使用histograms ,使用 histogram_quantile()
可以完全实现聚合。
scss
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) // GOOD.
此外,如果您的SLO发生变化并且您现在想要绘制第90个百分位,或者您想将最近10分钟而不是最近5分钟考虑在内,则只需调整上面的表达式即可,而无需重新配置客户端。
分位数估计的误差
分位数,无论是在客户端计算还是在服务端计算,都是一个估计值。理解分位数的误差对我们非常重要。
让我们从上面histogram 的例子继续,想象你通常的请求时长基本上都是220ms,或者换句话说,如果你绘制出了正确的histogram,你将会在220ms处看到一个超级大的尖峰。在上面配置的Prometheus直方图度量标准中,几乎所有观察结果以及第95个百分位都将落入标有" {le =" 0.3"}"的存储桶中,即从200ms到300ms的存储桶。 直方图实现可确保真实的第95个百分位数在200毫秒至300毫秒之间。 要返回单个值(而不是间隔),它会应用线性插值,在这种情况下将产生295ms。 计算得出的分位数给您的印象是您接近违反SLO,但实际上,第95个百分位数略高于220ms,这是与您的SLO相当舒适的距离。
我们的思想实验的下一步:更改后端路由会为所有请求持续时间增加固定的100ms。 现在,请求持续时间在320毫秒处急剧增加,几乎所有观察结果都会从300毫秒下降到450毫秒。 尽管正确值接近320ms,但第95个百分位数计算为442.5ms。 虽然您仅在SLO之外,但计算得出的第95分位数看起来要差得多。
Summary 就没有上述的问题,除非它在客户端侧使用了估计算法。不幸的是,如果你想要在多个客户端之间聚合,那你无法使用summary
幸运的是,由于你合适的选择了桶的边界,即使在观测值的分布非常锋利的尖刺的这个人为的例子,直方图能够正确识别,如果你是内或您的SLO之外。同样,分位数的实际值越接近我们的SLO(或换句话说,我们实际上最感兴趣的值),计算出的值就越准确。
现在让我们再次修改实验。在新的设置中,请求持续时间的分布在150ms处有一个尖峰,但不像以前那样尖锐,仅占观察值的90%。 10%的观察结果平均分布在150毫秒至450毫秒之间的长尾巴中。根据这种分布,第95个百分位数恰好在我们的300ms SLO处。使用直方图,由于第95个百分位数的值恰好与铲斗边界之一重合,因此计算得出的值是准确的。甚至略有不同的值也将是准确的,因为相关存储区中的(人为)均匀分布正是存储区中线性插值所假定的。
摘要报告的分位数误差现在变得更加有趣。摘要中的分位数误差是在φ维度中配置的。在我们的情况下,我们可能配置为0.95±0.01,即计算出的值将介于94%和96%之间。具有上述分布的第94位为270ms,第96位为330ms。摘要报告的第95个百分位数的计算值可以在270ms和330ms之间的任何位置,很遗憾,这是SLO内明显与SLO外明显之间的所有差异。
最重要的是:如果使用 summary ,则可以通过调整维度 来控制误差。如果使用直方图,则可以控制bucket分配 来调整误差(通过选择适当的桶布局)。
分布较宽时,φ的微小变化会导致观测值的较大偏差。分布较锐利时,观测值的较小间隔将覆盖φ的较大间隔。也就是说,整体宽泛的时候,分位数一点点变化,都会导致分位值的较大变化。整体较紧凑的时候,很小的分位值差值,就能覆盖很大的分位数比例。
两条经验法则:
1.如果需要聚合,请选择histogram。
2.否则,如果您对将要观察的值的范围和分布有所了解,请选择histogram 。无论值的范围和分布是什么,如果都需要准确的分位数,请选择summary。