Prometheus提供了一种叫做 PromQL
(Prometheus Query Language)的查询语言。可以直接在Prometheus的浏览器页面查询并显示结果(页面有查询框,以及 Table
和 Graph
两个用于显示结果的页签),也可以通过HTTP API来被其它系统所消费。
环境
- Ubuntu 22.04
- Prometheus 2.39.0-rc.0
表达式类型
- Instant vector - 瞬时向量,多个时间序列数据在某个特定时间戳上的值。这里“瞬时”指的是某一特定时间戳,“向量”指的是多个时间序列数据;
- Range vector - 范围向量,多个时间序列数据在某段特定时间范围内的值。这里“范围”指的是一段时间,“向量”指的是多个时间序列数据;
- Scalar - 标量,就是一个数值;
- String - 字符串(该类型还未被使用);
Table和Graph的区别
Graph不能显示Range vector。Graph只适用于:某个计算或者聚集的值,该值绑定在某一时间戳上,Graph会在各个时间戳上去计算该值,并以时间序列的方式显示该值。
Table无此限制。如果值是绑定在某一时间戳上,则显示最新值,如果是对应多个时间戳,则把所有值都显示出来。
Instant vector
以 promhttp_metric_handler_requests_total
为例,它有3个 code
label值,划分为3个时间序列数据。
直接查询 promhttp_metric_handler_requests_total
,在Table页面,会列出3个时间序列的最新值:
在Graph页面,会列出3个时间序列的所有值:
我们可以通过label去过滤时间序列,比如 promhttp_metric_handler_requests_total{code="200"}
,也就是只获取返回值为 200
的时间序列。按label过滤后,包含的时间序列数量可能会减少(本例中从3个变成了1个)。
多个过滤条件之间用 ,
分隔,比如 promhttp_metric_handler_requests_total{code="200", instance="localhost:9090"}
。
过滤条件可以用等于( =
)、不等于( !=
)、正则匹配( =~
和 !~
)。
Range vector
Range vector和instant vector的区别在于指定了时间范围,因此,对于Table页面,会显示时间范围内的所有值,而对于Graph页面则无效(Graph页面要求不能指定时间范围,它自己会管理时间范围)。
比如,要看 promhttp_metric_handler_requests_total
在过去2分钟内的所有数值: promhttp_metric_handler_requests_total[2m]
。
在Table页面会列出所有值:
而在Graph页面则会报错: Error executing query: invalid expression type "range vector" for range query, must be Scalar or instant Vector
。
聚集
注意聚集的一定是同一时间戳上的,不同维度的数据。
- sum (calculate sum over dimensions)
- min (select minimum over dimensions)
- max (select maximum over dimensions)
- avg (calculate the average over dimensions)
- group (all values in the resulting vector are 1)
- stddev (calculate population standard deviation over dimensions)
- stdvar (calculate population standard variance over dimensions)
- count (count number of elements in the vector)
- count_values (count number of elements with the same value)
- bottomk (smallest k elements by sample value)
- topk (largest k elements by sample value)
- quantile (calculate φ-quantile (0 ≤ φ ≤ 1) over dimensions)
比如对于 promhttp_metric_handler_requests_total
,我们知道,有3个时间序列,分别对应返回值为 200
、 500
、 503
的请求数量。
sum(promhttp_metric_handler_requests_total)
:在Table页面显示的是最新的3个值相加,在Graph页面显示的是在每个时间戳上3个值相加;count(promhttp_metric_handler_requests_total)
:维度的数量,也就是3,在Table页面显示最新的值(还是3),在Graph页面显示的是每个时间戳上的值(还是3);count_values("code", promhttp_metric_handler_requests_total)
:在code
这个维度上,不同的值的分布情况,假设在最新的时间戳上,code="200"
的值为390,而code="500"
和code="503"
的值都是0,则对于code
这个维度,值为390的序列有1个,而值为0的序列有2个,如下:
而在Graph页面,值为0的序列始终有2个,而 code="200"
的序列,其值一直在变化,所以如下:
topk
:显示数值最大的前k个时间序列,比如topk(2, promhttp_metric_handler_requests_total)
。code="200"
的最新值为427,而code="500"
和code="503"
的最新值都是0,所以按大小取前2个的话,如下:
quantile
:quantile(0.5, promhttp_metric_handler_requests_total)
比如0.5,表示中位数。现在有3个时间序列,最新值分别为427、0、0,所以中位数是0。
上面的聚集都是在所有dimension(label)上都聚集,我们也可以在指定的dimension上做聚集,方法是 by
和 without
。
sum by (code)(promhttp_metric_handler_requests_total)
:有点类似于SQL语句中的 sum(xxx) group by xxx
,本例中,区分不同code,不区分job和instance。
without
和 by
是互补的,上面这句就等同于:
sum without (instance, job)(promhttp_metric_handler_requests_total)
聚集over time
前面提到,聚集的一定是同一时间戳上的,不同维度的数据,要想聚集不同时间戳的数值,就要用到 xxx_over_time
:
- avg_over_time(range-vector)
- min_over_time(range-vector)
- max_over_time(range-vector)
- sum_over_time(range-vector)
- …
注意参数是range vector。
假设 code= "200"
的序列,最近1分钟的4个值分别为533,534,535,536(每15秒加1),则最近1分钟的总和为: sum_over_time(promhttp_metric_handler_requests_total[1m])
,如下:
注意,虽然参数是range vector,但结果是instantvector,所以可以在Graph页面显示。
rate
rate计算的是在给定时间范围内,每秒钟变化的比率,比如 promhttp_metric_handler_requests_total
在 code="200"
维度上,每15秒钟数值加1,则每秒钟变化的比率就是1/15,也就是0.067。
rate(promhttp_metric_handler_requests_total[3m])
注意,虽然参数是range vector,但结果是instant vector,所以可以在Graph页面显示。
rate()
函数默认是在每个时间戳上做计算(Table页面显示最新值,Graph页面显示时间序列),它的一个变种是返回range vector,多了2个参数,具体来说,是在哪些时间戳上做计算,以及做计算的频度,所以返回的是range vector。
rate(promhttp_metric_handler_requests_total[3m])[2m:1m]
:表示过去2分钟内,每1分钟做一次rate(rate的还是过去3分钟的每秒增长率):
注意返回的是range vector,不能在Graph页面显示。
参考
https://prometheus.io/docs/prometheus/latest/querying/