直方图和摘要是更复杂的指标类型。单个直方图或摘要不仅会创建大量时间序列,而且正确使用这些指标类型也更加困难。本节帮助您为用例选择和配置适当的指标类型。
有些库只支持这两种类型中的一种,或者它们对摘要的支持有限(缺少分位数计算)。
直方图和摘要都对观测值进行采样,通常是请求时长或响应大小。它们跟踪观测值的数量和观测值的总和,从而允许您计算观测值的平均值。请注意,观测值的数量(在 Prometheus 中显示为带有 _count
后缀的时间序列)本质上是一个计数器(如上所述,它只会单调递增)。观测值的总和(在 Prometheus 中显示为带有 _sum
后缀的时间序列)也表现得像一个计数器,只要没有负的观测值。显然,请求时长或响应大小永远不会是负数。然而,原则上,您可以使用摘要和直方图来观测负值(例如,摄氏温度)。在这种情况下,观测值的总和可能会下降,因此您不能再对其应用 rate()
。在那些极少数需要应用 rate()
且无法避免负观测值的情况下,您可以使用两个单独的摘要,一个用于正观测值,一个用于负观测值(后者符号反转),然后使用适当的 PromQL 表达式稍后合并结果。
要计算名为 http_request_duration_seconds
的直方图或摘要在过去 5 分钟内的平均请求时长,请使用以下表达式
rate(http_request_duration_seconds_sum[5m])
/
rate(http_request_duration_seconds_count[5m])
直方图的一个直接用法(但摘要不行)是计算落入特定观测值桶中的观测值数量。
您可能有一个 SLO,要求在 300ms 内处理 95% 的请求。在这种情况下,配置一个直方图,使其包含一个上限为 0.3 秒的桶。然后您可以直接表达在 300ms 内处理的请求的相对数量,并在值降至 0.95 以下时轻松触发告警。以下表达式按 job 计算了过去 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 倍)为上限的桶。示例:目标请求时长为 300ms。可容忍请求时长为 1.2s。以下表达式计算了过去 5 分钟内每个 job 的 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 表达式即时查询。 | 通常不可聚合。 |
注意表格中最后一项的重要性。让我们回到在 300ms 内处理 95% 请求的 SLO。这一次,您不想显示在 300ms 内处理的请求百分比,而是显示第 95 百分位数,即处理 95% 请求所需的时间长度。为此,您可以配置一个包含 0.95 分位数和(例如)5 分钟衰减时间的摘要,或者配置一个在 300ms 附近设置了一些桶的直方图,例如 {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 分钟的数据,您只需要调整上面的表达式,而无需重新配置客户端。
分位数,无论是在客户端还是服务器端计算,都是估计值。理解这种估计的误差非常重要。
继续上面的直方图示例,想象一下您通常的请求时长几乎都非常接近 220ms,换句话说,如果您能绘制“真实”的直方图,您会在 220ms 处看到一个非常尖锐的峰值。在按上述配置的 Prometheus 直方图指标中,几乎所有的观测值,因此也包括第 95 百分位数,将落入标记为 {le="0.3"}
的桶中,即从 200ms 到 300ms 的桶。直方图实现保证真实的第 95 百分位数在 200ms 到 300ms 之间。为了返回一个单一值(而不是一个区间),它应用线性插值,在这种情况下得到 295ms。计算出的分位数给您的印象是您接近违反 SLO,但实际上,第 95 百分位数略高于 220ms,距离您的 SLO 还有相当舒适的距离。
我们思想实验的下一步:后端路由的改变为所有请求时长增加了固定的 100ms。现在请求时长在 320ms 处有一个尖锐的峰值,几乎所有观测值将落入从 300ms 到 450ms 的桶中。计算出的第 95 百分位数为 442.5ms,尽管正确的值接近 320ms。虽然您只稍稍超出了 SLO,但计算出的第 95 分位数看起来要糟糕得多。
在这两种情况下,摘要都能毫无问题地计算出正确的百分位数值,至少如果它在客户端使用适当的算法(例如Go 客户端使用的算法)。不幸的是,如果您需要聚合来自多个实例的观测值,则无法使用摘要。
幸运的是,由于您选择了适当的桶边界,即使在观测值分布存在非常尖锐峰值的这个虚构示例中,直方图也能够正确判断您是否在 SLO 范围内。此外,分位数的实际值越接近我们的 SLO(换句话说,我们真正最感兴趣的值),计算出的值就越准确。
现在让我们再次修改实验。在新设置中,请求时长的分布在 150ms 处有一个峰值,但它不像之前那么尖锐,只占观测值的 90%。10% 的观测值在 150ms 到 450ms 之间均匀分布形成长尾。在这种分布下,第 95 百分位数恰好位于我们 300ms 的 SLO 处。使用直方图,计算出的值是准确的,因为第 95 百分位数的值恰好与其中一个桶的边界重合。即使是略微不同的值也仍然会很准确,因为相关桶内的(虚构的)均匀分布正是桶内线性插值所假设的。
摘要报告的分位数误差现在变得更有趣了。摘要中分位数的误差是在 φ 维度上配置的。在我们的例子中,我们可能配置了 0.95±0.01,即计算出的值将在第 94 到第 96 百分位数之间。上述分布下的第 94 分位数是 270ms,第 96 分位数是 330ms。摘要报告的第 95 百分位数的计算值可以在 270ms 到 330ms 之间的任意区间内,不幸的是,这正是明确在 SLO 内与明确超出 SLO 的全部区别。
总而言之:如果您使用摘要,您控制 φ 维度上的误差。如果您使用直方图,您控制观测值维度上的误差(通过选择适当的桶布局)。对于宽分布,φ 的微小变化会导致观测值的较大偏差。对于尖锐分布,一小段观测值区间覆盖了很大一段 φ 区间。
两条经验法则
如果您需要聚合,请选择直方图。
否则,如果您对观测值的范围和分布有了解,请选择直方图。如果您需要准确的分位数,无论值的范围和分布如何,请选择摘要。
实现它!欢迎代码贡献。一般来说,我们认为直方图比摘要更迫切需要。直方图也更容易在客户端库中实现,因此如果您不确定,我们建议首先实现直方图。
本文档是开源的。请通过提交 issues 或 pull requests 来帮助改进它。