01 引言
Prometheus查询语法官方文档地址:https://prometheus.io/docs/prometheus/latest/querying/basics/
Prometheus提供了一种名为PromQL (Prometheus query language)的函数式查询语言,允许用户实时选择和聚合时间序列数据,表达式的结果可以以图形的形式显示,通过HTTP API由外部系统使用。
在Prometheus页面,输入查询语句,可以看到类似于如下接界面:
Table
Graph
接下来,详细讲解下Prometheus的查询语法。
02 概念
2.1 表达式类型(Expression language data types)
在Prometheus的表达语言中,一个表达式或子表达式可以总结为以下四种类型:
- 瞬时向量(Instant vector ):一组时间序列,包含每个时间序列的单一样本,所有共享相同的时间戳;
- 范围向量(Range vector):一组时间序列,包含每个时间序列在一段时间内的数据点范围;
- 标量(Scalar):一个简单的数字浮点值;
- 字符串(String):一个简单的字符串值(目前未使用);
2.2 字面量(Literals)
2.2.1 String 字面量(String literals)
字符串可以指定为单引号、双引号或反引号中的文字。
PromQL遵循与Go相同的转义规则。在单引号或双引号中,反斜杠开始转义序列,转义序列后面可以是a、b、f、n、r、t、v或\。可以使用八进制(\nnn)或十六进制(\xnn, \unnnn和\ unnnnnn)提供特定字符。
反引号内不处理转义,与Go不同,Prometheus不会在反引号内丢弃换行符。
例子:
"this is a string" 'these are unescaped: \n \\ \t' `these are not unescaped: \n ' " \t`
2.2.2 Float 字面量(Float literals)
标量浮点值可以写成文字整型数或浮点数格式(仅包含空格以提高可读性):
[-+]?( [0-9]*\.?[0-9]+([eE][-+]?[0-9]+)? | 0[xX][0-9a-fA-F]+ | [nN][aA][nN] | [iI][nN][fF] )
举例:
23 -2.43 3.4e-9 0x8f -Inf NaN
2.3 时间序列选择器(Time series Selectors)
2.3.1 瞬时向量选择器(Instant vector selectors)
瞬时向量选择器:允许在给定的时间戳(即时)上选择一组时间序列和每个序列的单个样本值,在最简单的形式中,只指定一个度量名称。这将产生一个包含所有具有此度量名称的时间序列元素的即时向量。
这个例子选择了所有带有http_requests_total度量名称的时间序列:
http_requests_total
通过在花括号({})中添加逗号分隔的标签匹配器列表,可以进一步过滤这些时间序列。
举例:只选择那些带有http_requests_total度量名称的时间序列,并且作业标签设置为prometheus,它们的组标签设置为canary:
http_requests_total{job="prometheus",group="canary"}
也可以对标签值进行负匹配,或者将标签值与正则表达式进行匹配,存在以下标签匹配操作符:
- =: 选择与提供的字符串完全相等的标签;
- !=: 选择与提供的字符串不相等的标签;
- =~: 选择正则表达式与提供的字符串匹配的标签;
- !~: 选择与提供的字符串不匹配的标签;
正则表达式匹配是完全对应的,匹配env=~"foo"被视为env=~"^foo$"。
举例:这将为staging、testing和development环境以及除GET之外的HTTP方法选择所有http_requests_total时间序列:
http_requests_total{environment=~"staging|testing|development",method!="GET"}
注意:向量选择器必须指定一个名称或至少一个与空字符串不匹配的标签匹配器,下面的表达式是非法的:
{job=~".*"} # Bad!
相反,这些表达式是有效的,因为它们都有一个不匹配空标签值的选择器:
{job=~".+"} # Good! {job=~".*",method="get"} # Good!
通过与内部的__name__标签进行匹配,标签匹配器也可以应用于度量名称。例如,表达式http_requests_total等价于{__name__="http_requests_total"},除= (!=, =~, !~)也可以使用,以下表达式选择名称以job开头的所有指标:
{__name__=~"job:.*"}
指标名不能是关键字bool, on, ignoring, group_left和group_right中的一个。下面的表达式是非法的:
on{} # Bad!
解决这个限制的方法是使用__name__标签:
{__name__="on"} # Good!
Prometheus中的所有正则表达式都使用 RE2语法。
2.3.2 范量向量选择器(Range Vector Selectors)
范量标量:工作方式类似于瞬时向量,只是它们选择了从当前瞬间返回的一个样本范围。
从语法上讲,时间持续时间被附加在向量选择器末尾的方括号([])中,以指定应该为每个结果范围向量元素提取到多远的时间值。
举例:选择在过去5分钟内记录的所有时间序列的值,这些时间序列的度量名称为http_requests_total,作业标签设置为prometheus:
http_requests_total{job="prometheus"}[5m]
2.3.3 持续时间(Time Durations)
时间持续时间指定为一个数字,后面紧跟着下列的其中一个单位:
- ms :毫秒
- s :秒
- m : 分钟
- h : 小时
- d : 天(一天24小时)
- w : 周(一周7天)
- -y:年(一年365天)
时间持续时间可以进行串联组合(注意:单位必须从最长到最短订购,一个给定的单位在一段时间内只能出现一次),举例:
5h 1h30m 5m 10s
2.3.4 Offset修饰符(Offset modifier)
offset修饰符:允许在查询中更改单个瞬间和范围向量的时间偏移量。
举例:以下表达式返回相对于当前查询评估时间过去5分钟的http_requests_total的值:
http_requests_total offset 5m
注意:offset修饰符必须跟在选择器后面,即:
sum(http_requests_total{method="GET"} offset 5m) // GOOD.
如果这样是错误的:
sum(http_requests_total{method="GET"}) offset 5m // INVALID.
对于范围向量也是一样的,下面例子将返回http_requests_total一周前的5分钟速率:
rate(http_requests_total[5m] offset 1w)
对于时间上向前移动的比较,可以指定负偏移量:
rate(http_requests_total[5m] offset -1w)
2.3.5 @ modifier
@修饰符:允许在查询中改变单个瞬间向量和范围向量的计算时间,提供给@修饰符的时间是一个unix时间戳,用浮点文字描述。
例如,以下表达式返回http_requests_total在2021-01-04T07:40:00+00:00的值:
http_requests_total @ 1609746000
注意,@修饰符必须跟在选择器后面,举例:
sum(http_requests_total{method="GET"} @ 1609746000) // GOOD.
以下是错误写法:
sum(http_requests_total{method="GET"}) @ 1609746000 // INVALID.
对于范围向量也是一样的,下面表达式将返回http_requests_total在2021-01-04T07:40:00+00:00处的5分钟速率:
rate(http_requests_total[5m] @ 1609746000)
@修饰符在int64范围内支持上述浮点数字面量的所有表示。它还可以与offset修饰符一起使用,offset是相对于**@修饰符**时间应用的,而不管哪个修饰符先写,这两个查询将产生相同的结果。
# offset after @ http_requests_total @ 1609746000 offset 5m # offset before @ http_requests_total offset 5m @ 1609746000
另外,start()和end()也可以作为**@修饰符**的特殊值使用,对于范围查询,它们分别解析到范围查询的开始和结束,并对所有步骤保持相同,对于即时查询,start()和end()都解析为计算时间。
http_requests_total @ start() rate(http_requests_total[5m] @ end())
注意,@修饰符允许查询提前查看其计算时间。
2.4 子查询(Subquery)
子查询允许您对给定的范围即时查询,子查询的结果是一个范围向量。
语法:
<instant_query> '[' <range> ':' [<resolution>] ']' [ @ <float_literal> ] [ offset <duration> ]
2.5 操作符(Operators)
Prometheus支持许多二进制和聚合操作符,详情参考:https://prometheus.io/docs/prometheus/latest/querying/operators/
2.5.1 二元作符(Binary operators)
Prometheus的查询语言支持基本的逻辑和算术运算符。对于两个瞬时向量之间的运算,可以修改匹配行为。
2.5.1.1 算术二元操作符(Arithmetic binary operators)
在Prometheus中存在以下二进制算术运算符:
- + :(加)
- - :(减)
- * :(乘)
- - / :(除)
- % :(取余)
- ^ :(求幂)
二进制算术运算符定义在标量/标量、向量/标量和向量/向量值对之间:
- 两个标量之间的行为很明显:它们计算另一个标量,这个标量是应用于两个标量操作数的运算符的结果。
- 在瞬时向量和标量之间,将运算符应用于向量中每个数据样本的值。例如,如果一个时间序列瞬间向量乘以2,结果是另一个向量,其中原始向量的每个样本值乘以2,指标名称被删除。
- 在两个瞬时向量之间:对左边向量中的每一个元素以及右边向量中与其匹配的元素应用一个二元算术运算符。结果被传播到结果向量,分组标签成为输出标签集。指标名称被删除。右边向量中找不到匹配项的项不属于结果的一部分。
2.5.1.2 三角二元操作符符(Trigonometric binary operators)
以下是在弧度上工作的三角二元运算符,在普罗米修斯中存在:
- atan2(基于https://pkg.go.dev/math#Atan2)
三角运算符允许使用向量匹配对两个向量执行三角函数,这在普通函数中是不可用的。它们的作用与算术运算符相同。
2.5.1.3 比较二元操作符(Comparison binary operators)
比较二元操作符有如下:
- == (等于)
- != (不等于)
- (大于)
- < (小于)
- = (大于等于)
- <= (小于等于)
2.5.1.4 逻辑二元操作符(Logical/set binary operators)
逻辑二元操作符如下:
- and: (intersection)
- or: (union)
- unless:(complement)
2.5.2 向量匹配(Vector matching)
向量之间的运算试图为左边的每一个元素在右边的向量中找到一个匹配的元素。匹配行为有两种基本类型:一对一和多对一/一对多。
2.5.2.1 一对一向量匹配(One-to-one vector matches)
一对一从操作的每一边找到一个唯一的条目对。在默认情况下,这是一个遵循vector1 <操作符> vector2格式的操作。
如果两个条目具有完全相同的标签集和相应的值,则它们是匹配的。ignore关键字允许在匹配时忽略某些标签,而on关键字允许将考虑的标签集缩减为所提供的列表:
<vector expr> <bin-op> ignoring(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) <vector expr>
input举例:
method_code:http_errors:rate5m{method="get", code="500"} 24 method_code:http_errors:rate5m{method="get", code="404"} 30 method_code:http_errors:rate5m{method="put", code="501"} 3 method_code:http_errors:rate5m{method="post", code="500"} 6 method_code:http_errors:rate5m{method="post", code="404"} 21 method:http_requests:rate5m{method="get"} 600 method:http_requests:rate5m{method="del"} 34 method:http_requests:rate5m{method="post"} 120
query举例:
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
举例:下面将返回一个结果向量,其中包含每个方法在过去5分钟内的状态码为500的HTTP请求的比例。如果不忽略(代码),就不会有匹配,因为度量标准不共享相同的标签集。带有put和del方法的条目没有匹配,不会在结果中显示:
{method="get"} 0.04 // 24 / 600 {method="post"} 0.05 // 6 / 120
2.5.2.2 多对一和一对多向量匹配(Many-to-one and one-to-many vector matches)
多对一和一对多匹配指的是“一”面上的每个向量元素都可以与“多”面上的多个元素匹配。这必须使用group_left或group_right修饰符显式地请求,其中left/right决定哪个向量具有更高的基数。
<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr> <vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr>
与组修饰符一起提供的标签列表包含了要包含在结果度量中的来自“一侧”的附加标签。在一个标签上只能出现在一个列表中。结果向量的每个时间序列必须是唯一可识别的。
分组修饰符只能用于比较和算术。操作as和,除非和或默认匹配右向量中的所有可能条目。
示例查询:
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
在这种情况下,左向量每个方法标签值包含多个条目。因此,我们使用group_left来表示这一点。右边的元素现在与左边的多个具有相同方法标签的元素相匹配:
{method="get", code="500"} 0.04 // 24 / 600 {method="get", code="404"} 0.05 // 30 / 600 {method="post", code="500"} 0.05 // 6 / 120 {method="post", code="404"} 0.175 // 21 / 120
多对一和一对多匹配是应该仔细考虑的高级用例。通常,正确使用ignoring(<labels>)会提供所需的结果。
2.5.3 聚合运算符(Aggregation operators)
Prometheus支持以下内置的聚合操作符,可用于聚合单个瞬时向量的元素,从而生成一个具有聚合值的元素更少的新向量:
- 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)
这些操作符既可以用于对所有标签维度进行聚合,也可以通过包含without或by子句来保留不同的维度。这些子句可以在表达式之前或之后使用。
<aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>)
或者
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
2.5.4 二元操作符优先级(Binary operator precedence)
下面的列表显示了二元制操作符在普罗米修斯中从高到低的优先级:
- ^
- *, /, %, atan2
- +, -
- ==, !=, <=, <, >=, >
- and, unless
- or
具有相同优先级的操作符是左结合的。例如,2 * 3% % 2等于(2 * 3)% 2。然而^是右结合律,所以2 ^ 3 ^ 2等于2 ^ (3 ^ 2)
2.6 函数(Functions)
Prometheus支持对数据进行操作的几个函数,详情参考:https://prometheus.io/docs/prometheus/latest/querying/functions/
2.7 注释(Comments)
PromQL 支持以#开头的注释, 例如:
# This is a comment
2.8 陷阱(Gotchas)
2.8.1 过时(Staleness)
这里主要声明一些关于过期的描述,直接贴图:
2.8.2 避免慢查询和重载(Avoiding slow queries and overloads)
这里主要讲慢查询和重载的注意点,直接贴图:
03 举例
3.1 简单的时间序列选择(Simple time series selection)
返回带有度量http_requests_total的所有时间序列:
http_requests_total
返回带有指标http_requests_total和给定的作业和处理器标签的所有时间序列:
http_requests_total{job="apiserver", handler="/api/comments"}
返回同一向量的整个时间范围(在本例中为5分钟),使其成为一个范围向量:
http_requests_total{job="apiserver", handler="/api/comments"}[5m]
请注意,产生范围向量的表达式不能直接绘制图形,而是在表达式浏览器的表格(“Console”)视图中查看。
使用正则表达式,您可以仅为名称匹配特定模式的作业选择时间序列,在本例中,所有以server结尾的作业:
http_requests_total{job=~".*server"}
要选择除4xx以外的所有HTTP状态码,可以运行:
http_requests_total{status!~"4.."}
3.2 子查询(Subquery)
返回过去30分钟http_requests_total度量的5分钟速率,分辨率为1分钟。
rate(http_requests_total[5m])[30m:1m]
这是一个嵌套子查询的示例。派生函数的子查询使用默认分辨率。注意,不必要地使用子查询是不明智的。
max_over_time(deriv(rate(distance_covered_total[5s])[30s:5s])[10m:])
3.4 方法、操作符(Using functions, operators, etc.)
返回带有http_requests_total指标名称的所有时间序列的每秒速率,在过去5分钟内测量:
rate(http_requests_total[5m])
假设http_requests_total时间序列都有标签job(根据作业名称fanout)和instance(根据作业实例fanout),我们可能想要对所有实例的速率求和,这样我们得到更少的输出时间序列,但仍然保留作业维度:
sum by (job) ( rate(http_requests_total[5m]) )
如果我们有两个具有相同维度标签的不同度量,我们可以对它们应用二元运算符,两边具有相同标签集的元素将被匹配并传播到输出中。例如,该表达式返回MiB中每个实例的未使用内存(在一个虚构的集群调度程序上,暴露其运行的实例的这些指标):
(instance_memory_limit_bytes - instance_memory_usage_bytes) / 1024 / 1024
同样的表达式,但是通过应用求和,可以写成这样:
sum by (app, proc) ( instance_memory_limit_bytes - instance_memory_usage_bytes ) / 1024 / 1024
如果同一个虚构的集群调度程序暴露了每个实例的CPU使用率指标,如下所示:
instance_cpu_time_ns{app="lion", proc="web", rev="34d0f99", env="prod", job="cluster-manager"} instance_cpu_time_ns{app="elephant", proc="worker", rev="34d0f99", env="prod", job="cluster-manager"} instance_cpu_time_ns{app="turtle", proc="api", rev="4d3a513", env="prod", job="cluster-manager"} instance_cpu_time_ns{app="fox", proc="widget", rev="4d3a513", env="prod", job="cluster-manager"} ...
我们可以得到按应用程序(app)和进程类型(proc)分组的前3名CPU用户,如下所示:
topk(3, sum by (app, proc) (rate(instance_cpu_time_ns[5m])))
假设这个指标包含每个运行实例一个时间序列,你可以这样计算每个应用程序运行实例的数量:
count by (app) (instance_cpu_time_ns)
04 HTTP接口获取
当前稳定的HTTP API在Prometheus服务器的/ API /v1下是可访问的,详情参考:https://prometheus.io/docs/prometheus/latest/querying/api/
05 小结
本文主要讲解了PromSQL的构造以及每块组成部分的解析,具体还需要去详细查看官方的文档,本文就不再过细的描述了。谢谢大家的阅读,本文完!