直方图和摘要
注意本文档编写于原生直方图(在 Prometheus v2.40 中作为实验性功能添加)之前。一旦原生直方图接近成为稳定功能,本文档将被彻底更新。
直方图和摘要是更复杂的指标类型。单个直方图或摘要不仅会创建多个时间序列,而且正确使用这些指标类型也更加困难。本节将帮助您为您的用例选择和配置合适的指标类型。
客户端库支持
一些库只支持这两种类型中的一种,或者它们对摘要的支持有限(缺少分位数计算功能)。
观测次数和总和
直方图和摘要都对观测值进行采样,通常是请求持续时间或响应大小。它们跟踪观测次数和观测值的总和,从而允许您计算观测值的平均值。请注意,观测次数(在 Prometheus 中显示为带有 _count 后缀的时间序列)本质上是一个计数器(如上所述,它只会增加)。只要没有负的观测值,观测值的总和(显示为带有 _sum 后缀的时间序列)的行为也像一个计数器。显然,请求持续时间或响应大小永远不会是负数。但原则上,您可以使用摘要和直方图来观测负值(例如摄氏温度)。在这种情况下,观测值的总和可能会减少,因此您不能再对其应用 rate()。在那些罕见的情况下,当您需要应用 rate() 并且无法避免负的观测值时,您可以使用两个独立的摘要,一个用于正观测值,一个用于负观测值(后者符号反转),然后使用适当的 PromQL 表达式组合结果。
要从名为 http_request_duration_seconds 的直方图或摘要中计算过去 5 分钟的平均请求持续时间,请使用以下表达式:
rate(http_request_duration_seconds_sum[5m])
/
rate(http_request_duration_seconds_count[5m])
Apdex 分数
直方图的一个直接用途(摘要则不行)是计算落入特定观测值桶中的观测次数。
您可能有一个 SLO(服务水平目标),要求在 300 毫秒内处理 95% 的请求。在这种情况下,可以配置一个直方图,使其有一个上限为 0.3 秒的桶。然后,您可以直接表示在 300 毫秒内处理的请求的相对数量,并在该值低于 0.95 时轻松发出告警。以下表达式按作业计算过去 5 分钟内处理的请求。请求持续时间是使用名为 http_request_duration_seconds 的直方图收集的。
sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
/
sum(rate(http_request_duration_seconds_count[5m])) by (job)
您可以用类似的方式近似计算众所周知的 Apdex 分数。配置一个桶,其上限为目标请求持续时间,再配置另一个桶,其上限为可容忍的请求持续时间(通常是目标请求持续时间的 4 倍)。例如:目标请求持续时间为 300 毫秒。可容忍的请求持续时间为 1.2 秒。以下表达式得出了过去 5 分钟内每个作业的 Apdex 分数:
(
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)
请注意,我们将两个桶的总和除以了。原因是直方图的桶是累积的。le="0.3" 的桶也包含在 le="1.2" 的桶中;将其除以 2 可以修正这一点。
这个计算与传统的 Apdex 分数并不完全匹配,因为它在满意和可容忍部分的计算中包含了错误。
分位数
您可以使用摘要和直方图来计算所谓的 φ-分位数,其中 0 ≤ φ ≤ 1。φ-分位数是在 N 个观测值中排在第 φ*N 位的观测值。φ-分位数的例子:0.5-分位数被称为中位数。0.95-分位数是第 95 百分位数。
摘要和直方图的本质区别在于,摘要在客户端计算流式 φ-分位数并直接暴露它们,而直方图暴露分桶的观测计数,分位数的计算是在服务器端使用 histogram_quantile() 函数从直方图的桶中计算出来的。
这两种方法有许多不同的影响:
| 直方图 | 摘要 | |
|---|---|---|
| 所需配置 | 选择适合预期观测值范围的桶。 | 选择所需的 φ-分位数和滑动窗口。之后无法计算其他 φ-分位数和滑动窗口。 |
| 客户端性能 | 观测成本非常低,因为它们只需要增加计数器。 | 由于流式分位数计算,观测成本较高。 |
| 服务器性能 | 服务器必须计算分位数。如果即席计算耗时过长(例如在大型仪表盘中),您可以使用记录规则。 | 服务器端成本低。 |
时间序列数量(除了 _sum 和 _count 系列) | 每个配置的桶一个时间序列。 | 每个配置的分位数一个时间序列。 |
| 分位数误差(详见下文) | 误差在观测值维度上受限于相关桶的宽度。 | 误差在 φ 维度上受限于一个可配置的值。 |
| φ-分位数和滑动时间窗口的指定 | 通过 Prometheus 表达式进行即席配置。 | 由客户端预先配置。 |
| 聚合 | 通过 Prometheus 表达式进行即席配置。 | 一般来说,不可聚合。 |
请注意表格中最后一项的重要性。让我们回到在 300 毫秒内处理 95% 请求的 SLO。这次,您不想显示在 300 毫秒内处理的请求百分比,而是想显示第 95 百分位数,即您处理了 95% 请求所用的请求持续时间。为此,您可以配置一个具有 0.95-分位数和(例如)5 分钟衰减时间的摘要,或者配置一个在 300 毫秒附近有几个桶的直方图,例如 {le="0.1"}、{le="0.2"}、{le="0.3"} 和 {le="0.45"}。如果您的服务以多个实例复制运行,您将从每个实例收集请求持续时间,然后您希望将所有数据聚合成一个总体的第 95 百分位数。然而,聚合来自摘要的预计算分位数很少有意义。在这种特殊情况下,对分位数求平均值会产生统计上无意义的值。
avg(http_request_duration_seconds{quantile="0.95"}) // BAD!
使用直方图,通过 histogram_quantile() 函数,聚合是完全可能的。
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) // GOOD.
此外,如果您的 SLO 发生变化,现在您想绘制第 90 百分位数,或者您想考虑过去 10 分钟而不是过去 5 分钟,您只需调整上面的表达式,而无需重新配置客户端。
分位数估计的误差
分位数,无论是在客户端还是服务器端计算,都是估计值。理解这种估计的误差很重要。
继续上面的直方图示例,想象一下您通常的请求持续时间几乎都非常接近 220 毫秒,换句话说,如果您能绘制“真实”的直方图,您会在 220 毫秒处看到一个非常尖锐的峰值。在上面配置的 Prometheus 直方图指标中,几乎所有的观测值,因此也包括第 95 百分位数,都将落入标记为 {le="0.3"} 的桶中,即 200 毫秒到 300 毫秒的桶。直方图的实现保证了真实的第 95 百分位数在 200 毫秒到 300 毫秒之间。为了返回单个值(而不是一个区间),它应用了线性插值,在这种情况下得出 295 毫秒。计算出的分位数给您的印象是您接近违反 SLO,但实际上,第 95 百分位数略高于 220 毫秒,与您的 SLO 还有一个相当舒适的距离。
我们思想实验的下一步:后端路由的改变给所有请求持续时间增加了一个固定的 100 毫秒。现在请求持续时间在 320 毫秒处有一个尖峰,几乎所有的观测值都将落入 300 毫秒到 450 毫秒的桶中。计算出的第 95 百分位数是 442.5 毫秒,尽管正确的值接近 320 毫秒。虽然您只比您的 SLO 超出了一点点,但计算出的第 95 百分位数看起来要糟糕得多。
摘要在两种情况下都能毫无问题地计算出正确的百分位数值,至少如果它在客户端使用了适当的算法(比如 Go 客户端使用的算法)。不幸的是,如果您需要聚合来自多个实例的观测值,就不能使用摘要。
幸运的是,由于您对桶边界的适当选择,即使在这个人为构造的观测值分布中存在非常尖锐峰值的例子中,直方图也能够正确地识别您是否在 SLO 范围内。而且,分位数的实际值越接近我们的 SLO(换句话说,我们实际最感兴趣的值),计算出的值就越准确。
现在让我们再次修改实验。在新的设置中,请求持续时间的分布在 150 毫秒处有一个峰值,但它不像之前那样尖锐,只包含了 90% 的观测值。10% 的观测值均匀地分布在 150 毫秒到 450 毫秒之间的一个长尾中。在这种分布下,第 95 百分位数恰好在我们的 SLO 300 毫秒处。使用直方图,计算出的值是准确的,因为第 95 百分位数的值恰好与其中一个桶边界重合。即使是稍微不同的值仍然会是准确的,因为(人为构造的)相关桶内的均匀分布正是桶内线性插值所假设的。
摘要报告的分位数误差现在变得更有趣了。摘要中的分位数误差是在 φ 维度上配置的。在我们的例子中,我们可能配置了 0.95±0.01,即计算出的值将在第 94 和第 96 百分位数之间。对于上述分布,第 94 百分位数是 270 毫秒,第 96 百分位数是 330 毫秒。摘要报告的第 95 百分位数的计算值可以在 270 毫秒到 330 毫秒之间的任何地方,这不幸地涵盖了从明确在 SLO 范围内到明确在 SLO 范围外的全部差异。
底线是:如果您使用摘要,您控制的是 φ 维度上的误差。如果您使用直方图,您控制的是观测值维度上的误差(通过选择合适的桶布局)。对于宽泛的分布,φ 的微小变化会导致观测值的巨大偏差。对于尖锐的分布,一个小的观测值区间会覆盖一个大的 φ 区间。
两条经验法则
-
如果您需要聚合,请选择直方图。
-
否则,如果您对将要观测的值的范围和分布有所了解,请选择直方图。如果您需要一个准确的分位数,而不管值的范围和分布如何,请选择摘要。
如果我的客户端库不支持我需要的指标类型,我该怎么办?
自己实现它!我们欢迎代码贡献。一般来说,我们预计对直方图的需求比摘要更迫切。直方图在客户端库中也更容易实现,因此如果存有疑问,我们建议首先实现直方图。