prometheus-----长尾问题,跳变问题,数据外推问题

长尾问题

问题根因:

因为它用平均值将峰值削平了,无法反映时间窗口内样本数据的快速变化。

使用rate或者increase函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。

例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。

irate函数相比于rate函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate的这种灵敏度反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用rate函数。

irate同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。

counter跳变问题

Counter 因为是一个只递增的值,所以它可以判断数字下降的问题,比如现在请求的 Count 数是 1000,然后下次 Prometheus 来抓取发现变成了 20,那么 Prometheus 就知道,真实的数据不可能是 20,因为请求数是不可能下降的。所以它会将这个点认为是 1020。

然后用 Counter 也可以解决多次读的问题,服务器上的 /metrics,可以使用 cURL 和 grep 等工具实时查看,不会改变数据。

Counters(计数型)是Prometheus 的一种指标类型,其值只增不减,且代表累计的总计数,比如“总共已处理了多少请求?”或者“用了多少秒来处理请求?”由于counter的值取决于追踪和公开过程的初始(重新)启动时间,因此它的绝对值几乎是没有用的。所以在根据counter绘图或做任何事之前,通常要借助rate(), irate()或 increase() 函数来查看它的rate。

如何处理Counter重置(跳变):

尽管counter一般只会叠加,但是当追踪它们的过程重置时,它们也会重置为0 。为了不把这些重置解释成实际的负rate,和counter相关的函数具有检测和处理这些重置的逻辑:如当在提供的时间窗口下迭代样本时,函数会检查是否有任何样本的值比前一个低,并将这种情况解释为counter重置。Counter在重置后总是从0开始,那么根据这个假设,这些函数只是将新的采样值加到之前看到的采样值上,以补偿重置。

在这里插入图片描述
注意:跳变问题是计算时解决,不是落库时解决:

Prometheus 支持在 Counter 的数据有下降之后自动处理的,比如服务器重启了,metric 重新从 0 开始。这个其实不是在存储的时候做的,Prometheus 就会忠实地存储
采集到的数据,但是在计算 rate 的时候,就会识别出来这个下降。

数据外推

rate()和 increase()两个函数的外推经常让人混淆。

例如,对于只有整数增量的counter,increase() 也可能返回非整数结果,如2.5883。这是因为increase() 是在规定时间窗口的总范围内得到counter增长量的近似值(比如increase(foo[5m])中的5分钟)。但实际上,时间窗口中的第一个和最后一个样本,永远不会与规定时间窗口的开始和结束100%重合。因此increase() (以及rate())会在窗口与窗口的界限中外推第一个和最后一个数据点之间的斜率,以得出一个平均而言更接近整个窗口内预期增长的数值(如果在窗口边界确实有样本)。

在这里插入图片描述

注意:这种外推是有一些例外的

当一个序列好像在规定的采样周期开始或结束时,我们不希望向该序列终止的方向外推太远。rate() 和increase()函数推测,当第一个或最后一个样本距离各自的窗口边界大于窗口中样本之间平均间隔的1.1倍时,序列会在窗口中开始或结束。在这种情况下,外推只向窗口边界延伸半个平均样本区间,而不是全部。同样,函数会避免外推至负值,因为counter总是从0开始,永远不会是负值。相反,只有达到预期值0时,外推才会发生。

注意:不外推情况

由于irate()只看两个样本间每秒的增长,所以它不做这种外推。

counter与gauge

你可以将 Gauge 理解为“无状态的”,即类型是 Gauge 的 metric 不需要关心历史的值,只需要记录当前的值是多少就可以了。比如当前的内存值,当前的 CPU 使用率。当然,如果你想要查询历史的值,依然是可以查到的。只不过对于每一个时间点的“内存使用量”这个 Gauge,不包含历史的数据。

counter这个数字是只增不减的,用 Counter 最合适了。因为每一个时间点的总请求数都会包含之前时间点的请求数,所以可以理解成它是一个“有状态的”

典型的 Counter 举例:

服务器服务的请求数,服务器收到了多少包。这个数字是只增不减的,用 Counter 最合适了。因为每一个时间点的总请求数都会包含之前时间点的请求数,所以可以理解成它是一个“有状态的”。
使用 Counter 记录每一个时间点的“总数”,然后除以时间,就可以得到总的QPS,packets/s 等数据;
通过计算 Counter 两个时间点的“差额”,然后除以时间,就可以得到某个时间段的QPS,packets/s 等数据;

可否用 Gauge 来代替 Counter 呢?

服务器暴露一个 HTTP 服务,Prometheus 来访问这个 HTTP 接口来获取 metrics 的数据。
如果使用 Gauge 来表示上面的 pk/s 数据的话,只能使用这种方案:使用这个 Metric 记录自从上次抓取过后收到的 Packet 总数(或者直接记录 Packet/s ,原理是一样的)。每次 Prometheus 来抓取数据之后,就将这个值重置为 0. 这样的实现就类似 Gauge 了。

这种实现的缺点有:

1、抓取数据本质是 GET 操作,但是这个 GET 操作却会修改数据(将 metric 重置为 0),
所以会带来很多隐患,比如一个服务每次只能由一个 Prometheus 来抓取,不能扩展;
不能 cURL 这个 /metrics 来进行 debug,因为会影响真实的数据,等等。

2、如果服务器发生了重启,数据将会清零,会丢失数据(虽然 Counter 也没有从本质上解决这个问题)。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐