PromQL调研学习笔记

315 阅读9分钟

Prometheus 提供了一种功能表达式语言 PromQL,允许用户实时选择和汇聚时间序列数据。表达式的结果可以在浏览器中显示为图形,也可以显示为表格数据,或者由外部系统通过 HTTP API 调用。

参考文档:prometheus.io/docs/promet…

表达式语言数据类型

  • 瞬时向量(Instant vector) - 一组时间序列,每个时间序列包含单个样本,它们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式。
1.选择指标名称为 http_server_duration_milliseconds_bucket 的所有时间序列,可以通过向花括号({})里附加一组标签来进一步过滤时间序列。
http_server_duration_milliseconds_bucket
2.选择指标名称为 http_server_duration_milliseconds_bucket,HTTP 方法为 GET 或者 POST 的时间序列
http_server_duration_milliseconds_bucket{http_method=~"GET|POST"}

没有指定标签的标签过滤器会选择该指标名称的所有时间序列。

所有的 PromQL 表达式必须至少包含一个指标名称,或者一个不会匹配到空字符串的标签过滤器。

  • 区间向量(Range vector) - 一组时间序列,每个时间序列包含一段时间范围内的样本数据。
1.选择在过去 5 分钟内指标名称为 http_server_duration_milliseconds_bucket,http_method 标签值为 GET|POST 的所有时间序列
http_server_duration_milliseconds_bucket{http_method=~"GET|POST"}[5m]

2.选择在过去 5 分钟之前指标名称为 http_server_duration_milliseconds_bucket,http_method 标签值为 GET|POST 的所有时间序列
http_server_duration_milliseconds_bucket{http_method=~"GET|POST"} offset 5m

3.指标 http_server_duration_milliseconds_bucket 一周前的 5 分钟之内的 HTTP 请求量的增长率
rate(http_server_duration_milliseconds_bucket[5m] offset 1w)
  •  标量(Scalar) - 一个浮点型的数据值。标量浮点值可以字面上写成 -[.(digits)] 的形式。eg: -2.43
  • 字符串(String) - 一个简单的字符串值。

操作符

标量(scalar):一个固定的数据

向量 (vector):使用瞬时向量表达式能够获取到一个包含多个时间序列的集合

  • 算术二元运算符

scalar/scalar(标量/标量)在两个标量之间进行数学运算,得到的结果也是标量。

vector/scalar(向量/标量)在向量和标量之间,这个运算符会作用于这个向量的每个样本值上。例如:如果一个时间序列瞬时向量除以 2,操作结果也是一个新的瞬时向量,且度量指标名称不变, 它是原度量指标瞬时向量的每个样本值除以2。

vector/vector(向量/向量)运算符会依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。同时新的时间序列将不会包含指标名称。

  • 布尔运算符

应用于 scalar/scalar(标量/标量)、vector/scalar(向量/标量),和vector/vector(向量/向量)。

瞬时向量和标量之间的布尔运算,这个运算符会应用到某个当前时刻的每个时序数据上,如果一个时序数据的样本值与这个标量比较的结果是 false,则这个时序数据被丢弃掉,如果是 true, 则这个时序数据被保留在结果中。

瞬时向量与瞬时向量直接进行布尔运算时,同样遵循默认的匹配模式:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行相应的操作,如果没找到匹配元素,或者计算结果为 false,则直接丢弃。如果匹配上了,则将左边向量的度量指标和标签的样本数据写入瞬时向量。如果提供了 bool 修饰符,那么比较结果是 0的时序数据被丢弃掉,而比较结果是 1 的时序数据(只保留左边向量)被保留。

  • 集合运算符

vector1 and vector2 会产生一个由 vector1 的元素组成的新的向量。该向量包含 vector1 中完全匹配 vector2 中的元素组成。

vector1 or vector2 会产生一个新的向量,该向量包含 vector1 中所有的样本数据,以及 vector2 中没有与 vector1 匹配到的样本数据。

vector1 unless vector2 会产生一个新的向量,新向量中的元素由 vector1 中没有与 vector2 匹配的元素组成。

匹配模式

向量与向量之间进行运算操作时会基于默认的匹配规则:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。

  • 一对一匹配

在操作符两边表达式标签不一致的情况下,可以使用 on(label list) 或者 ignoring(label list)来修改便签的匹配行为。使用 ignoreing 可以在匹配时忽略某些便签。而 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

//使用PromQL表达式
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
//该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在所有请求中的比例。如果没有使用 ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。
结果如下:
{method="get"}  0.04            //  24 / 600
{method="post"} 0.05            //   6 / 120
同时由于 method 为 put 和 del 的样本找不到匹配项,因此不会出现在结果当中。
  • 多对一和一对多

多对一和一对多两种匹配模式指的是“一”侧的每一个向量元素可以与"多"侧的多个元素匹配的情况。在这种情况下,必须使用 group 修饰符:group_left 或者 group_right 来确定哪一个向量具有更高的基数(充当“多”的角色)。

多对一和一对多两种模式一定是出现在操作符两侧表达式返回的向量标签不一致的情况。因此需要使用 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

提醒:group 修饰符只能在比较和数学运算符中使用。在逻辑运算 and,unless 和 or 操作中默认与右向量中的所有元素进行匹配。

二元运算符优先级

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

具有相同优先级的运算符是满足结合律的(左结合)。例如,2 * 3 % 2 等价于 (2 * 3) % 2。运算符 ^ 例外,^满足的是右结合,例如,2 ^ 3 ^ 2 等价于 2 ^ (3 ^ 2)

函数

聚合函数

<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]

//指标 http_requests_total 的时间序列的标签集为 application, instance, 和 group,我们可以通过以下方式计算所有 instance 中每个 application 和 group 的请求总量:
sum(http_requests_total) without (instance)
sum(http_requests_total) by (application, group)

//topk 和 bottomk 则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。
topk(5, http_requests_total)

//quantile 用于计算当前样本数据值的分布情况 quantile(φ, express) ,其中 0 ≤ φ ≤ 1
例如,当 φ 为 0.5 时,即表示找到当前样本数据中的中位数:
quantile(0.5, http_requests_total)

其中只有 count_values, quantile, topk, bottomk 支持参数(parameter)。

without 用于从计算结果中移除列举的标签,而保留其它标签。by 则正好相反,结果向量中只保留列出的标签,其余标签则移除。通过 without 和 by 可以按照样本的问题对数据进行聚合。

内置函数

prometheus.io/docs/promet…

histogram_quantile(φ float, b instant-vector)从 bucket 类型的向量 b 中计算 φ (0 ≤ φ ≤ 1) 分位数(百分位数的一般形式)的样本的最大值。

可以使用 rate() 函数来指定分位数计算的时间窗口。

一个直方图指标名称为 employee_age_bucket_bucket,要计算过去 10 分钟内 第 90 个百分位数,请使用以下表达式:
histogram_quantile(0.9, rate(employee_age_bucket_bucket[10m]))

# histogram_quantile() 函数必须包含 le 标签
以下表达式根据 job 标签来对第 90 个百分位数进行聚合:
histogram_quantile(0.9, sum(rate(employee_age_bucket_bucket[10m])) by (job, le))

示例

查询过去5分钟内平均耗时最长的十个接口

topk(10, histogram_quantile(0.95,rate(http_server_duration_milliseconds_bucket[1h])))

查询过去5分钟内耗时超过300ms的接口(按照耗时高低排序)

sort_desc(histogram_quantile(0.95,rate(http_server_duration_milliseconds_bucket[1h])) > 30)

查询调用最频繁的10个接口

topk(10, sort_desc(sum(http_server_duration_milliseconds_count{}) by (http_route)))

查询过去5分钟内调用失败的接口占比

sum(rate(http_server_duration_milliseconds_bucket{http_status_code != "200"}[5m])) / sum(rate(http_server_duration_milliseconds_bucket[5m]))