GH Page 地址
Prometheus 有两种 query:instant query、range query。本文要讲的就是 range query 中的 step 参数。
range query 是非常常见的一种 query,看看它有哪些参数:
query=<string>: PromQL 表达式。
start=<rfc3339 | unix_timestamp>: 时间范围的开始。
end=<rfc3339 | unix_timestamp>: 时间范围的结束。
step=<duration | float>: 查询解析度(query resolution)。
timeout=<duration>: 执行超时。这个参数是可选的。
在 Prometheus expression browser 里看到的是这样的:
注意到上图中的 Res 框里没有给值,没有给的话 Prometheus 会自动给一个值,这个值在图示右上角可以看到。
step 对于查询结果的影响
Prometheues 在对 PromQL 表达式求值的逻辑是这样的(详见这个 issue 里的回答):
对于 [start, end] 时间区间,从 start 开始,以 step 为长度,把时间区间分成若干段
对每个段进行求值
举例:start=10,end=20,step=2,那么就会有 ts=10,ts=12,ts=14,ts=16,ts=18,ts=206 段,然后为这 6 个段进行求值。求值方式视乎表达式中 Time series selector 的类型而定。
PromQL 有两种 Time series selector:instant vector selector 和 range vector selector。下面将分别讲解:
Instant vector selector
形如下面的就是 Instant vector selector,x 是 metric 的名字。
x
Prometheus 在对每段 Instant vector selector 求值的逻辑是这样的:
从该段的 timestamp(含)往前找,取第一个找到的 data point 的值。如果有一个 data point 的 timestamp== 该段的 timestamp,则直接使用该 data point。
如果该段 timestamp 往前的 5 分钟范围内没有找到任何 data point,则该段无值。
下面这张图解释了上面逻辑:
图中的绿点是 Prometheus 实际存储的数据,按照时间轴从左到右排列。蓝点是根据 step 参数的求值结果。
当 data point 间隔比 step 更大的时候会发生下图这种情况:
可以看到有两个段的求值结果来自于同一个 data point。
Range vector selector
形如下面的就是 Range vector selector,x 是 metric 的名字,方括号里的是 range duration。
x[5m]
range vector select 返回的是当前 timestamp 之前的 range duration 内的所有 data point。range vector 是不能直接用做绘图的,你得用某些 function 把 range vector 转换成 instant vector 才行,比如 rate()。
下图解释了是如何对 Range vector selector 进行分段求值的:
step 和 rate duration
step 和 range duration 是独立的两个参数,在某些情况下两者的值存在某种限制条件,这里例举 rate()来说明。rate()的作用是获得一个 range-vector 的每秒平均增长率。
如果 step=10m 而 range duration=5m,那么 rate 在计算的时候会丢失一半的数据,两个分段之间的 data point 有一半没有被纳入计算。前面那张图就存在数据丢失的情况,有一个 data point 被漏掉了。
因此在使用 rate()时,range duration 得大于等于 step。
而如果是 irate(),这个限制则是 range duration 不得大于 step(详见 Brian Brazil 的 Presentation)。
Grafana 中的 step 参数
在 Grafana 中并没有直接提供 step 参数,而是这两个参数:min step 和 resolution(文档在这里)。min step 故名思义设定的是 step 的最小值,那么 resolution 是什么呢?
大家都知道 Grafana 都是用来画图的,比如下面这张图 Y 轴是值,X 轴则是时间线,因此在 X 轴方向的每个像素都代表了一个 timestamp。
resolution 就是用来根据像素来计算 step 的一个参数。下面用 6 个像素以及它们的 timestamp 来说明:
x=1,ts=0; x=2,ts=5; x=3,ts=10; x=4,ts=15; x=5,ts=20; x=6,ts=25
resolution=1/ 1 时,那么 step 就是相邻像素所代表的 timestamp 的差,即 5;
resolution=1/ 2 时,那么 step 就是相隔 1 个像素的两个像素的 timestamp 的差,即 10;
resolution=1/ 3 时,那么 step 就是相隔 2 个像素的两个像素的 timestamp 的差,即 15;
以此类推
而每个像素所代表的 timestamp 受两个因素影响:
查询所定义的时间范围
Graph 的宽度(单位:像素)
所以在 Grafana 发起的查询中 step 参数是动态的。其实这也是很合理的,因为只有这样才能够在 Graph 宽度小的时候绘图更粗糙(即 step 更大),Graph 宽度大的时候绘图更精细(即 step 更小,但是不能小于 min step)。实际发起的请求的 step 参数你可以在 Graph 的 Query Inspector 里看到:
但是我们之前不说过了 rate()的 range duration 不能小于 step 吗?那么把 range duration 给固定值的化就不太好了,怎么办呢?你可以使用 Grafana 提供的内置变量 $__interval,它代表的 Grafana 就是计算出来的 step 的值。比如这样就能够将 range duration 和 step 保持一致了(更多内置变量可以见这里):
rate(x[$__interval])
所以,你想自己实验一把
如果你想自己动手实验,但是又苦于无法制造干净的假数据,那么可以参考这篇文章推荐的方法。