Prometheus 查询操作符
二进制操作
Prometheus 的查询语言支持基本的逻辑和算术运算符。对于两个瞬时向量之间的运算,可以修改匹配行为。
计算二元操作符
普罗米修斯中存在以下二进制运算符:
- +(添加)
- -(减)
- *(乘法)
- /(除法)
- %(取余)
- ^(取幂)
二元算术运算符定义在标量/标量、向量/标量和向量/向量值对之间。
在两个标量之间,行为是明显的,它们计算到另一个标量,该标量是应用于两个标量操作数的操作符的结果。
在瞬时向量和标量之间,该运算符应用于向量中的每个数据样本的值。例如,如果一个时间序列瞬时向量乘以2,则结果是另一个向量,其中原始向量的每个样本值都乘以2。指标名称将被删除。
在两个瞬时向量之间,对左边向量中的每一项和右边向量中的匹配元素应用一个二元算术运算符。结果被传播到结果向量中,分组标签成为输出标签集。指标名称将被删除。在右边向量中找不到匹配条目的条目不属于结果的一部分。
比较二元操作符
Prometheus中存在以下二进制比较运算符:
==
(等于)!=
(不等于)>
(大于)<
(小于)>=
(大于等于)<=
(小于等于)
比较运算符定义在标量/标量、向量/标量和向量/向量值对之间。默认情况下,它们进行过滤。它们的行为可以通过在操作符后提供bool来修改,这将返回值0或1,而不是过滤。
在两个标量之间,必须提供bool修饰符,这些操作符会产生另一个标量,根据比较结果,标量要么为0 (false),要么为1 (true)。
在瞬时向量和标量之间,这些操作符被应用到向量中的每个数据样本的值,而比较结果为假的向量元素将从结果向量中删除。如果提供了bool修饰符,则要删除的向量元素的值为0,而要保留的向量元素的值为1。如果提供了bool修饰符,则删除指标名称。
在两个瞬时向量之间,这些操作符默认情况下作为过滤器,应用于匹配的条目。表达式不为真或在表达式的另一端没有找到匹配的向量元素将从结果中删除,而其他元素将传播到结果向量中,分组标签将成为输出标签集。如果提供了bool修饰符,那么将被删除的向量元素的值为0,而将被保留的向量元素的值为1,分组标签再次成为输出标签集。如果提供了bool修饰符,则删除指标名称。
逻辑操作符
这些逻辑运算符只在瞬时向量之间定义:
- and (与)
- or (或)
- unless (非)
Vector1 和 vector2 的结果是一个由 Vector1 中的元素组成的向量,而vector2 中的元素具有完全匹配的标签集。其他元素被删除。指标名称和值是从左边的向量传递过来的。
Vector1 或 vector2 的结果是包含 Vector1 的所有原始元素(标签集+值)和vector2中没有匹配标签集的所有元素的 vector。
除非 vector2 的结果是由 Vector1 的元素组成的向量,而在 vector2 中没有完全匹配的标签集。删除两个向量中所有匹配的元素。
向量匹配
向量与向量之间进行运算操作时会基于默认的匹配规则:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。
接下来将介绍在PromQL中有两种典型的匹配模式:一对一(one-to-one),多对一(many-to-one)或一对多(one-to-many)。
一对一向量匹配
一对一的向量匹配会从操作符的每一边表达式找到一对唯一的样本值。在默认情况下,这是遵循vector1 <operator> vector2
格式的操作。
在操作符两边表达式标签不一致的情况下,可以使用on(label list)或者ignoring(label list)来修改便签的匹配行为。ignoring 关键字允许在匹配时忽略某些标签,而on关键字允许将考虑的标签集减少到提供的列表:
<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr>
比如这样的数据样本:
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
下面我们看一个查询的例子。
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
这该表达式会返回在过去5分钟内,HTTP请求状态码为500的在所有请求中的比例。如果没有使用ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。
同时由于method为put和del的样本找不到匹配项,因此不会出现在结果当中。
{method="get"} 0.04 // 24 / 600
{method="post"} 0.05 // 6 / 120
一对多或者多对一向量匹配
多对一和一对多匹配是指“一”上的每个向量元素都可以与“多”上的多个元素匹配。这必须使用 group_left
或 group_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>
多对一和一对多两种模式一定是出现在操作符两侧表达式返回的向量标签不一致的情况。因此需要使用ignoring和on修饰符来排除或者限定匹配的标签列表。
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
该表达式中,左向量method_code:http_errors:rate5m包含两个标签method和code。而右向量method:http_requests:rate5m中只包含一个标签method,因此匹配时需要使用ignoring限定匹配的标签为code。 在限定匹配标签后,右向量中的元素可能匹配到多个左向量中的元素 因此该表达式的匹配模式为多对一,需要使用group修饰符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(
聚合运算符
Prometheus支持以下内置聚合操作符,这些操作符可用于聚合单个瞬时向量的元素,从而生成一个包含更少元素、具有聚合值的新向量:
- sum (在当前维度求和)
- min (在当前维度取最小值)
- max (在当前维度取最大值)
- avg (在当前维度计算平均值)
- group (结果向量中的所有值都是1)
- stddev (在当前维度计算标准差)
- stdvar (在当前维度计算标准方差)
- count (计算向量中元素的个数)
- count_values (等于某值的元素个数)
- bottomk (按样本值计算的最小k个元素)
- topk (按样本值计算的最大k个元素)
- quantile (计算φ-quantile(0≤φ≤1)的值,也就是百分位数)
这些操作符既可以用于对所有标签维度进行聚合,也可以通过包含without或by子句来保留不同的维度。这些子句可以用在该查询的前面或后面。
<aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>)
# 或者
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
Label list
是一个未加引号的标签列表,后面可能包含一个逗号,即(label1, label2)和(label1, label2,)都是有效的语法。
without
删除结果向量中列出的标签,并且保留输出中的其他标签。by
则相反,删除by
子句中没有列出的标签,即使它们的标签值在向量的所有元素中是相同的。
parameter
参数仅用于count_values、quantile、topk 和bottomk。
count_values
为每个唯一的样本值输出一个时间序列。每个系列都有一个附加标签。该标签的名称由聚合参数给出,标签值是唯一的样本值。每个时间序列的值是该样本值出现的次数。
topk
和bottomk
与其他聚合器不同的是,在结果向量中返回输入样本的子集(包括原始标签)。by
和without
仅用于抽取输入向量。
quantile
计算φ-quantile
,即在所有维度的N个度量值中排名为φ*N的值。以φ为聚合参数。例如,分位数(0.5,…)计算中位数,分位数(0.95,…)第95个百分位。
二进制运算符优先级
下面这个列表列举了 Prometheus 二进制操作符的优先顺序,按照从高到低的顺序排列。
1. ^
2. *, /, %
3. +, -
4. ==, !=, <=, <, >=, >
5. and, unless
6. or
相同优先级的操作符是从左向右进行计算的。例如,2 * 3 % 2
相当于(2 * 3)% 2
。但是^
是从右向左的,所以2 ^ 3 ^ 2
等于2 ^ (3 ^ 2)
。
当多个指标查询的数据同时小于某个值后显示
node_filesystem_avail_bytes and node_filesystem_avail_bytes < 2000000000
当可用空间大于200000或者小于2500000都会显示
node_filesystem_avail_bytes > 200000 or node_filesystem_avail_bytes < 2500000
当第一个值的标签和第二个值的标签不匹配的情况下会输出
up{instance="192.168.1.20:9100",job="node"} unless up{instance="192.168.1.21:9100",job="node"}
当标签相同时则不输出
up{instance="192.168.1.20:9100",job="node"} unless up{instance="192.168.1.20:9100",job="node"}