Histogram 和 Summary
Histogram 和 Summary 是更复杂的指标类型。而且单个 Histogram 或 Summary 会创建大量的时间序列,使得正确地使用这些指标类型更加困难。本节将帮助你选择并配置适用于特定用例适当的指标类型。
库支持
首先,请检查代码库库对 Histogram 和 Summary 的支持。
一些库仅支持其中一种类型,或者它们只以有限的方式支持 Summary(缺乏 quantiles 支持)。
观察的数量与总和
Histogram 和 Summary 都会采样观察值,这通常是请求持续时间和响应大小。它们跟踪观察的_数量_和观察值的_总和_,允许你快速计算观察值的平均值。请注意,显示在 Prometheus 中的带有 _count
后缀的时间序列本质上是 Counter(如上所述,它只会增加)。带有 _sum
后缀的时间序列行为类似于 Counter,只要没有负观察值。显然,请求持续时间和响应大小永远不会是负数。原则上,你可以使用 Histogram 和 Summary 来观察负值(例如,摄氏温度)。在这种情况下,观察值的总和可以下降,所以你无法再在时间序列上应用 rate()
函数。在需要应用 rate()
并且无法避免负观察值的极少数情况下,你可以使用两个单独的 Summary,一个用于正观察值,另一个用于负观察值(后者带相反的符号),并在稍后的合适 Prometheus 表达式中结合结果。
要从名为 http_request_duration_seconds
的 Histogram 或 Summary 中计算过去 5 分钟内的平均请求持续时间,可以使用以下表达式:
Apdex 得分
Histogram 最直接的使用方法是统计特定观察值范围内的观察次数。
假设你有一个服务级别指标(SLO),要求 95% 的请求在 300 毫秒内完成。在这种情况下,可以配置一个 Histogram,其上限为 0.3 秒的桶,使用表达式计算 300 毫秒内处理完成的请求比例,当结果值低于 0.95 时发出警报。通过使用 histogram_quantile()
函数,可以为每个作业在过去 5 分钟内计算 http_request_duration_seconds
的 Apdex 得分:
注意我们将两个桶的总和除以 2。原因在于 Histogram 桶是累积的。le="0.3"
桶也包含在 le="1.2"
桶中;除以 2 可以纠正这一情况。
此计算不一定完全符合传统 Apdex 得分,因为它在计算满意部分和可容忍部分时存在误差。
分位数(Quantiles)
你可以使用 Histogram 和 Summary 计算所谓的 φ-分位数,其中 0 ≤ φ ≤ 1。φ-分位数是排名在 N 观察值中第 φ*N 的观察值。例如:φ=0.5 分位数被称为中位数。φ=0.95 分位数是第 95 百分位数。
Histogram 和 Summary 之间的主要区别在于:Summary 在客户端侧上计算流式 φ-分位数,并直接暴露这些值;而 Histogram 在服务器端上基于桶中的观察计数使用 histogram_quantile()
函数计算分位数,。
两种方法具有不同的含义:
Histogram | Summary | |
---|---|---|
必需的配置 | 为预期的观察值范围选择合适的桶。 | 选择所需的 φ-分位数和滑动窗口。其他 φ-分位数和滑动窗口无法在之后计算。 |
客户端性能 | 观察成本非常低,只需递增 Counter 即可。 | 观察成本昂贵,因为需要进行流式分位数计算。 |
服务器性能 | 服务器必须计算分位数。应使用记录规则(recording rules)应对临时计算耗时过长的情况(例如,在大型仪表盘中)。 | 服务器端成本低。 |
额外的时间序列数量 | 每个配置的存储桶有一个时间序列。 | 每个配置的分位数有一个时间序列。 |
φ-量化误差(参见下文了解详情) | 误差受相关观察上存储桶宽度维度的限制. | 误差受限于 φ 维度,由可配置的值限制。 |
φ-分位数和滑动时间窗口的指定 | Prometheus 表达式。 | 由客户端预先配置。 |
聚合 | Prometheus 表达式。 | 通常不可聚合。 |
请注意表格中最后一项的重要性。让我们回到以下 SLO:在 300 毫秒内完成 95% 的请求。这次,你不希望显示请求在 300 毫秒内完成的比例,而是 95% 请求的请求持续时间,即 95% 的请求在多长的时间内完成。要这样做,你可以配置一个分位数为 0.95 的 Summary,并且例如使用 5 分钟的衰减时间,或者你可以配置一个Histogram,桶的配置围绕 300 毫秒,例如 {le="0.1"}
、{le="0.2"}
、{le="0.3"}
和 {le="0.45"}
。如果你的服务有多个实例副本,则你会收集来自所有实例的请求持续时间,并将其聚合为总体 95% 请求。然而,从一个 Summary 聚合预计算的分位数很少是有意义的。在这种特定情况下,平均分位数会产生统计上不合理的值。
使用 Histogram,聚合可以通过 histogram_quantile()
函数完美地实现。
此外,如果你的 SLO 发生变化并想绘制 90% 的百分位数,或者想考虑过去 10 分钟而不是过去的 5 分钟,你只需要调整上述表达式,而不必重新配置客户端。
分位数估算的误差
无论是在客户端还是服务器端计算,分位数都是估算的,理解这种估算的误差很重要。
以上面提到的 Histogram 示例为例,想象一下你的常规请求持续时间几乎全都在220毫秒左右,换句话说,如果你可以绘制“真实”的 Histogram,你会看到一个非常尖锐的峰值在220毫秒处。在上文中的 Prometheus Histogram 指标配置中,几乎所有观测结果,以及产生的第95百分位数,都将落入标有{le="0.3"}
的桶中,即从200毫秒到300毫秒的桶。Histogram 可以确保真实的第95百分位数在200毫秒和300毫秒之间。为了返回一个单一值(而不是区间),它应用线性插值,在这种情况下给出了295毫秒的结果。分位数结果可能会让你觉得违反了 SLO 的要求,但实际上,第95百分位数略高于220毫秒,与 SLO 保持着还可以接受的差距。
实验的下一步:后端路由更改,向所有请求持续时间添加了固定的100毫秒。现在,请求持续时间有一个非常尖锐的峰值在320毫秒,并且几乎所有的观测结果都将落入从300毫秒到450毫秒的桶。第95百分位数被计算为442.5毫秒,尽管正确的值接近于320毫秒。虽然你只略微偏离了 SLO,但计算得出的第95分位数结果看起来要糟糕得多。
在上述的两种情况下,如果在客户端侧使用适当的算法(例如 Go 客户端使用的方法),Summary 都能够计算出准确的分位数。不幸的是,如果你需要聚合来自多个实例的观察结果时,无法使用 Summary。
幸运的是,由于你正确选择了桶边界,即使在这个人为的例子中分布观察值的峰值非常尖锐,Histogram 仍然能够准确地识别是否在或超出你的 SLO。此外,实际值与我们真正最感兴趣的值越接近(或者说,我们真正关心的值),计算出的值就越准确。
现在让我们再修改一次实验。在新的实验中,请求持续时间的分布有一个在150毫秒处的峰值,但没有那么尖锐,并且只占了90%的观察结果,剩下的10%的观察结果均匀分布在150毫秒到450毫秒之间的长尾中。在这种分布下,第95百分位数恰好与我们的SLO的300毫秒相匹配。此时使用 Histogram,计算出的值是准确的,因为第95百分位数恰好与一个桶边界相匹配。即使略有不同的值也会保持准确,因为相关桶内的(人为构造的)均匀分布正是桶内线性插值的预设前提。
现在,由 Summary 报告的分位数误差就变得有趣起来了。在 Summary 中分位数的误差是在φ的维度上配置的。在我们的例子中,我们可能已经配置了0.95±0.01,即计算出的值将在第94百分位数和第96百分位数之间。根据上述分布,第94百分位数是270毫秒,第96百分位数是330毫秒。由 Summary 给出的第95百分位数计算值可以在270毫秒到330毫秒之间的区间内任意位置,不幸的是,这正是在 SLO 内与明确超出 SLO 的区别所在。
简而言之:如果你使用 Summary,你可以控制在φ维度上的误差。如果你使用 Histogram,则可以通过选择合适的桶布局来控制在观测值维度上的误差。对于广泛的分布,φ的小变化会导致观测值的大偏差。对于尖锐分布,观察值的一个小区间则覆盖了φ的大区间。
两条经验法则:
- 如果你需要聚合,选择 Histogram。
- 否则,如果你对将被观察到的值的范围和分布有所了解,请选择 Summary。另外,如果你需要准确的分位数,无论值的范围和分布如何,请选择 Summary。
如果我的客户端库不支持我需要的指标类型,我可以做些什么?
你可以实现它,Prometheus 欢迎代码贡献!通常情况下,我们预估 Histogram 比 Summary 更为迫切需要。Histogram 在客户端库中实现起来也相对更容易,因此我们建议如果你不确定,可以优先实现 Histogram。
该文档基于 Prometheus 官方文档翻译而成。