运算符

二元运算符

Prometheus 的查询语言支持基本的逻辑和算术运算符。对于两个瞬时向量之间的运算,可以修改匹配行为

算术二元运算符

Prometheus 中存在以下二元算术运算符

  • + (加法)
  • - (减法)
  • * (乘法)
  • / (除法)
  • % (取模)
  • ^ (幂/指数)

二元算术运算符定义在标量/标量、向量/标量和向量/向量值对之间。

在两个标量之间,行为是显而易见的:它们求值为另一个标量,该标量是将运算符应用于两个标量操作数的结果。

在瞬时向量和标量之间,运算符应用于向量中每个数据样本的值。例如,如果一个时间序列瞬时向量乘以 2,结果是另一个向量,其中原始向量的每个样本值都乘以 2。指标名称被删除。

在两个瞬时向量之间,二元算术运算符应用于左侧向量中的每个条目及其在右侧向量中的匹配元素。结果会传播到结果向量中,分组标签成为输出标签集。指标名称被删除。在右侧向量中找不到匹配条目的条目不包含在结果中。

三角二元运算符

以下以弧度为单位的三角二元运算符存在于 Prometheus 中

三角运算符允许使用向量匹配在两个向量上执行三角函数,这在普通函数中是不可用的。它们的行为方式与算术运算符相同。

比较二元运算符

以下二元比较运算符存在于 Prometheus 中

  • == (等于)
  • != (不等于)
  • > (大于)
  • < (小于)
  • >= (大于或等于)
  • <= (小于或等于)

比较运算符定义在标量/标量、向量/标量和向量/向量值对之间。默认情况下,它们进行过滤。可以通过在运算符后提供 bool 来修改其行为,这将为值返回 01 而不是过滤。

在两个标量之间,必须提供 bool 修饰符,并且这些运算符会产生另一个标量,该标量为 0 (false) 或 1 (true),具体取决于比较结果。

在瞬时向量和标量之间,这些运算符应用于向量中每个数据样本的值,并且比较结果为 false 的向量元素将从结果向量中删除。如果提供了 bool 修饰符,则将被删除的向量元素的值为 0,而将被保留的向量元素的值为 1。如果提供了 bool 修饰符,则指标名称将被删除。

在两个瞬时向量之间,这些运算符默认情况下充当过滤器,应用于匹配的条目。表达式不为 true 或在表达式的另一侧找不到匹配项的向量元素将从结果中删除,而其他元素将传播到结果向量中,分组标签成为输出标签集。如果提供了 bool 修饰符,则将被删除的向量元素的值为 0,而将被保留的向量元素的值为 1,分组标签再次成为输出标签集。如果提供了 bool 修饰符,则指标名称将被删除。

逻辑/集合二元运算符

这些逻辑/集合二元运算符仅在瞬时向量之间定义

  • and (交集)
  • or (并集)
  • unless (补集)

vector1 and vector2 生成一个向量,其中包含 vector1 的元素,这些元素在 vector2 中具有完全匹配的标签集。其他元素被删除。指标名称和值从左侧向量继承。

vector1 or vector2 生成一个向量,其中包含 vector1 的所有原始元素(标签集 + 值),以及 vector2 中在 vector1 中没有匹配标签集的所有元素。

vector1 unless vector2 生成一个向量,其中包含 vector1 的元素,这些元素在 vector2 中没有完全匹配的标签集。两个向量中所有匹配的元素都被删除。

向量匹配

向量之间的运算尝试为左侧向量中的每个条目在右侧向量中找到一个匹配元素。有两种基本的匹配行为类型:一对一和多对一/一对多。

向量匹配关键字

这些向量匹配关键字允许在具有不同标签集的序列之间进行匹配,提供

  • on
  • ignoring

提供给匹配关键字的标签列表将确定如何组合向量。示例可以在一对一向量匹配多对一和一对多向量匹配中找到

分组修饰符

这些分组修饰符启用多对一/一对多向量匹配

  • group_left
  • group_right

可以为分组修饰符提供标签列表,其中包含要包含在结果指标中的“一”侧的标签。

多对一和一对多匹配是高级用例,应仔细考虑。通常,正确使用 ignoring(<labels>) 可以提供期望的结果。

分组修饰符只能用于比较算术andunlessor 运算默认与右侧向量中的所有可能条目匹配。

一对一向量匹配

一对一从运算的每一侧找到唯一的条目对。在默认情况下,这是遵循格式 vector1 <operator> vector2 的运算。如果两个条目具有完全相同的标签集和对应的值,则它们匹配。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 分钟内每种方法的状态代码为 500 的 HTTP 请求的比例。如果没有 ignoring(code),则不会有匹配,因为指标不共享相同的标签集。方法为 putdel 的条目没有匹配项,不会显示在结果中

{method="get"}  0.04            //  24 / 600
{method="post"} 0.05            //   6 / 120

多对一和一对多向量匹配

多对一一对多匹配是指“一”侧的每个向量元素可以与“多”侧的多个元素匹配的情况。这必须使用 group_leftgroup_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>

分组修饰符 一起提供的标签列表包含要包含在结果指标中的“一”侧的其他标签。对于 on,标签只能出现在其中一个列表中。结果向量的每个时间序列都必须是唯一可识别的。

示例查询

method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m

在这种情况下,左侧向量包含每个 method 标签值的多个条目。因此,我们使用 group_left 指示这一点。右侧的元素现在与左侧具有相同 method 标签的多个元素匹配

{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

聚合运算符

Prometheus 支持以下内置聚合运算符,可用于聚合单个瞬时向量的元素,从而生成具有聚合值的元素较少的新向量

  • sum (计算维度总和)
  • min (选择维度最小值)
  • max (选择维度最大值)
  • avg (计算维度平均值)
  • group (结果向量中的所有值均为 1)
  • stddev (计算维度总体标准差)
  • stdvar (计算维度总体标准方差)
  • count (计算向量中元素的数量)
  • count_values (计算具有相同值的元素的数量)
  • bottomk (按样本值最小的 k 个元素)
  • topk (按样本值最大的 k 个元素)
  • quantile (计算维度上的 φ-分位数 (0 ≤ φ ≤ 1))
  • limitk (采样 n 个元素)
  • limit_ratio (如果 𝑟 > 0,则按大约 𝑟 比率采样元素;如果 𝑟 = -(1.0 - 𝑟),则采样此类样本的补集)

这些运算符可以用于聚合所有标签维度,或者通过包含 withoutby 子句来保留不同的维度。这些子句可以在表达式之前或之后使用。

<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_valuesquantiletopkbottomklimitklimit_ratio 是必需的。

count_values 为每个唯一的样本值输出一个时间序列。每个序列都有一个附加标签。该标签的名称由聚合参数给出,标签值是唯一的样本值。每个时间序列的值是该样本值出现的次数。

topkbottomk 与其他聚合器不同,因为输入样本的子集(包括原始标签)在结果向量中返回。bywithout 仅用于对输入向量进行分桶。

limitklimit_ratio 也返回输入样本的子集,包括结果向量中的原始标签,这些是实验性运算符,必须使用 --enable-feature=promql-experimental-functions 启用。

quantile 计算 φ-分位数,即在聚合维度 N 个指标值中排名第 φ*N 的值。φ 作为聚合参数提供。例如,quantile(0.5, ...) 计算中位数,quantile(0.95, ...) 计算第 95 个百分位数。对于 φ = NaN,返回 NaN。对于 φ < 0,返回 -Inf。对于 φ > 1,返回 +Inf

示例

如果指标 http_requests_total 具有按 applicationinstancegroup 标签展开的时间序列,我们可以通过以下方式计算每个应用程序和组在所有实例中看到的 HTTP 请求总数

sum without (instance) (http_requests_total)

这等效于

 sum by (application, group) (http_requests_total)

如果我们只对所有应用程序中看到的 HTTP 请求总数感兴趣,我们可以简单地写

sum(http_requests_total)

要计算每个构建版本运行的二进制文件数量,我们可以写

count_values("version", build_version)

要获得所有实例中最大的 5 个 HTTP 请求计数,我们可以写

topk(5, http_requests_total)

要采样 10 个时间序列,例如检查标签及其值,我们可以写

limitk(10, http_requests_total)

要确定性地采样大约 10% 的时间序列,我们可以写

limit_ratio(0.1, http_requests_total)

鉴于 limit_ratio() 实现了确定性采样算法(基于标签的哈希值),你可以获得上述样本的补集,即大约 90%,但精确地说是那些未被 limit_ratio(0.1, ...) 返回的样本,使用

limit_ratio(-0.9, http_requests_total)

你还可以使用此功能来验证 avg() 是否是样本值的代表性聚合,方法是检查两个样本子集的平均值之间的差异与标准差相比是否“小”。

abs(
  avg(limit_ratio(0.5, http_requests_total))
  -
  avg(limit_ratio(-0.5, http_requests_total))
) <= bool stddev(http_requests_total)

二元运算符优先级

以下列表显示了 Prometheus 中二元运算符的优先级,从最高到最低。

  1. ^
  2. *, /, %, atan2
  3. +, -
  4. ==, !=, <=, <, >=, >
  5. and, unless

同一优先级级别的运算符是左结合的。例如,2 * 3 % 2 等效于 (2 * 3) % 2。但是 ^ 是右结合的,所以 2 ^ 3 ^ 2 等效于 2 ^ (3 ^ 2)

原生直方图运算符

原生直方图是一项实验性功能。摄取原生直方图必须通过功能标志启用。一旦摄取了原生直方图,就可以对其进行查询(即使在再次禁用功能标志之后)。但是,对原生直方图的运算符支持仍然非常有限。

即使涉及到直方图样本,逻辑/集合二元运算符也能按预期工作。它们仅检查向量元素的存在,并且不会根据元素的样本类型(浮点型或直方图)更改其行为。count 聚合运算符的工作方式类似。

两个原生直方图之间的二元 +- 运算符,以及聚合原生直方图的 sumavg 聚合运算符完全受支持。即使所涉及的直方图具有不同的桶布局,也会自动适当转换桶,以便可以执行运算。(使用当前支持的桶模式,这始终是可能的。)如果任一运算符必须聚合直方图样本和浮点样本的混合,则相应的向量元素将完全从输出向量中删除。

二元 * 运算符在原生直方图和浮点型之间以任何顺序工作,而二元 / 运算符可以在原生直方图和浮点型之间以该确切顺序使用。

所有其他运算符(以及上述运算符的未提及情况)的行为方式没有意义。它们要么将直方图样本视为值 0 的浮点样本,要么(在标量和向量之间的算术运算的情况下)保持直方图样本不变。此行为将在原生直方图成为稳定功能之前更改为有意义的行为。

本文档是开源的。请通过提交问题或拉取请求来帮助改进它。