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