直方图和摘要
注意此文档是在原生直方图(在 Prometheus v2.40 中作为实验性功能添加,并在 v3.8 中稳定)出现之前编写的。我们打算在不久的将来彻底更新此文档。
直方图和摘要是更复杂的指标类型。单个直方图或摘要不仅会创建大量时间序列,而且正确使用这些指标类型也更加困难。本节将帮助您为用例选择和配置合适的指标类型。
库支持
有些库只支持这两种类型中的一种,或者它们对摘要的支持有限(缺少分位数计算)。
观测次数和总和
直方图和摘要都会对观测值进行采样,通常是请求持续时间或响应大小。它们会跟踪观测次数和观测值的总和,使您能够计算观测值的平均值。请注意,观测次数(在 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 表达式即时计算。 | 通常不可聚合 。 |
请注意表格最后一项的重要性。让我们回到 SLO,即在 300 毫秒内响应 95% 的请求。这一次,您不想显示在 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。此外,分位数实际值越接近我们的 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 以外的所有差异。
底线是:如果您使用摘要,您将控制 φ 维度上的误差。如果您使用直方图,您将控制观测值维度上的误差(通过选择合适的分桶布局)。对于宽分布,φ 的小变化会导致观测值的大偏差。对于尖锐分布,小的观测值区间覆盖大的 φ 区间。
两个经验法则
-
如果您需要聚合,请选择直方图。
-
否则,如果您对将要观测到的值的范围和分布有所了解,请选择直方图。如果您需要一个准确的分位数,无论值的范围和分布如何,请选择摘要。
如果我的客户端库不支持我需要的指标类型,我该怎么办?
实现它!欢迎贡献代码。总的来说,我们认为直方图比摘要更迫切需要。直方图也更容易在客户端库中实现,因此如果您不确定,我们建议首先实现直方图。