跳转至

PromQL 基础

在继续深入学习 PromQL 查询细节之前,我们先来看看 PromQL 查询的一些理论基础。

嵌套结构

与 SQL 查询语言(SELECT * FROM ...)不同,PromQL 是一种嵌套的函数式语言,就是我们要把需要查找的数据描述成一组嵌套的表达式,每个表达式都会评估为一个中间值,每个中间值都会被用作它上层表达式中的参数,而查询的最外层表达式表示你可以在表格、图形中看到的最终返回值。比如下面的查询语句:

histogram_quantile(  # 查询的根,最终结果表示一个近似分位数。
  0.9,  # histogram_quantile() 的第一个参数,分位数的目标值
  # histogram_quantile() 的第二个参数,聚合的直方图
  sum by(le, method, path) (
    # sum() 的参数,直方图过去5分钟每秒增量。
    rate(
      # rate() 的参数,过去5分钟的原始直方图序列
      demo_api_request_duration_seconds_bucket{job="demo"}[5m]
    )
  )
)

PromQL 表达式不仅仅是整个查询,而是查询的任何嵌套部分(比如上面的rate(...)部分),你可以把它作为一个查询本身来运行。在上面的例子中,每行注释代表一个表达式。

结果类型

在查询 Prometheus 时,有两个 类型 的概念经常出现,区分它们很重要。

  • 抓取目标报告的指标类型:counter、gauge、histogram、summary。
  • PromQL 表达式的结果数据类型:字符串、标量、瞬时向量或区间向量。

PromQL 实际上没有直接的指标类型的概念,只关注表达式的结果类型。每个 PromQL 表达式都有一个类型,每个函数、运算符或其他类型的操作都要求其参数是某种表达式类型。例如,rate() 函数要求它的参数是一个区间向量,但是 rate() 本身评估为一个瞬时向量输出,所以 rate() 的结果只能用在期望是瞬时向量的地方。

PromQL 中可能的表达式类型包括:

  • string(字符串):字符串只会作为某些函数(如 label_join()label_replace())的参数出现。
  • scalar(标量):一个单一的数字值,如 1.234,这些数字可以作为某些函数的参数,如 histogram_quantile(0.9, ...)topk(3, ...),也会出现在算术运算中。
  • instant vector(瞬时向量):一组标记的时间序列,每个序列有一个样本,都在同一个时间戳,瞬时向量可以由 TSDB 时间序列选择器直接产生,如node_cpu_seconds_total,也可以由任何函数或其他转换来获取。
node_cpu_seconds_total{cpu="0", mode="idle"}    19165078.75 @ timestamp_1
node_cpu_seconds_total{cpu="0", mode="system"}    381598.72 @ timestamp_1
node_cpu_seconds_total{cpu="0", mode="user"}    23211630.97 @ timestamp_1
  • range vector(区间向量):一组标记的时间序列,每个序列都有一个随时间变化的样本范围。在 PromQL 中只有两种方法可以生成区间向量:在查询中使用字面区间向量选择器(如 node_cpu_seconds_total[5m]),或使用子查询表达式(如 <expression>[5m:10s]),当想要在指定的时间窗口内聚合一个序列的行为时,区间向量非常有用,就像 rate(node_cpu_seconds_total[5m]) 计算每秒增加率一样,在 node_cpu_seconds_total 指标的最后 5 分钟内求平均值。
node_cpu_seconds_total{cpu="0", mode="idle"}    19165078.75 @ timestamp_1,  19165136.3 @ timestamp_2, 19165167.72 @ timestamp_3
node_cpu_seconds_total{cpu="0", mode="system"}  381598.72   @ timestamp_1,   381599.98 @ timestamp_2,   381600.58 @ timestamp_3
node_cpu_seconds_total{cpu="0", mode="user"}    23211630.97 @ timestamp_1, 23211711.34 @ timestamp_2, 23211748.64 @ timestamp_3

注意:但是指标类型呢?如果你已经使用过 PromQL,你可能知道某些函数仅适用于特定类型的指标!例如,histogram_quantile() 函数仅适用于直方图指标, rate() 仅适用于计数器指标,deriv() 仅适用于 Gauge。但是 PromQL 实际上并没有检查你是否传入了正确类型的指标——这些函数通常会运行并为错误类型的输入指标返回一些无意义的数据,这取决于用户是否传入了遵守某些假设的时间序列(比如在直方图的情况下有一个有意义的 le 标签,或者在计数器的情况下单调递增)。

查询类型和评估时间

PromQL 查询中对时间的引用只有相对引用,比如 [5m],表示过去 5 分钟,那么如何指定一个绝对的时间范围,或在一个表格中显示查询结果的时间戳?在 PromQL 中,这样的时间参数是与表达式分开发送到 Prometheus 查询 API 的,确切的时间参数取决于你发送的查询类型,Prometheus 有两种类型的 PromQL 查询:瞬时查询和区间查询。

瞬时查询

瞬时查询用于类似表格的视图,你想在一个时间点上显示 PromQL 查询的结果。一个瞬时查询有以下参数:

  • PromQL 表达式
  • 一个评估的时间戳

在查询的时候可以选择查询过去的数据,比如 foo[1h] 表示查询 foo 序列最近 1 个小时的数据,访问过去的数据,对于计算一段时间内的比率或平均数等聚合会非常有用。

瞬时查询

在 Prometheus 的 WebUI 界面中表格视图中的查询就是瞬时查询,API 接口 /api/v1/query?query=xxxx&time=xxxx 中的 query 参数就是 PromQL 表达式,time 参数就是评估的时间戳。瞬时查询可以返回任何有效的 PromQL 表达式类型(字符串、标量、即时和范围向量)。

下面来看一个瞬时查询的示例,看看它是如何进行评估工作的。比如 http_requests_total 在指定的时间戳来评估表达式,http_requests_total 是一个瞬时向量选择器,它可以选择该时间序列的最新样本,最新意味着查询最近 5 分钟的样本数据。

如果我们在一个有最近样本的时间戳上运行此查询,结果将包含两个序列,每个序列都有一个样本:

即时

注意每个返回的样本输出时间戳不再是原始样本被采集的时间戳,而会被设置为评估的时间戳。

如果在时间戳之前有一个 >5m 的间隙,这个时候如果我们执行相同的查询:

空数据

这个情况下查询的结果将返回为空,因为很显然在最近 5 分钟内没有能够匹配的样本。

区间查询

区间查询主要用于图形,想在一个指定的时间范围内显示一个 PromQL 表达式,范围查询的工作方式与即时查询完全相同,这些查询在指定时间范围的评估步长中进行评估。当然,这在后台是高度优化的,在这种情况下,Prometheus 实际上并没有运行许多独立的即时查询。

区间查询包括以下一些参数:

  • PromQL 表达式
  • 开始时间
  • 结束时间
  • 评估步长

在开始时间和结束时间之间的每个评估步长上评估表达式后,单独评估的时间片被拼接到一个单一的区间向量中。区间查询允许传入瞬时向量类型或标量类型的表达式,但始终返回一个范围向量(标量或瞬时向量在一个时间范围内被评估的结果)。

在 Prometheus 的 WebUI 界面中图形视图中的查询就是区间查询,API 接口 /api/v1/query_range?query=xxx&start=xxxxxx&end=xxxx&step=14 中的 query 参数就是 PromQL 表达式,start 为开始时间,end 为结束时间,step 为评估的步长。

区间查询

比如把上面的 http_requests_total 表达式作为一个范围查询来进行评估,它的评估结果如下所示:

区间查询

注意每个评估步骤的行为与独立的瞬时查询完全一样,而且每个独立的瞬时查询都没有查询的总体范围的概念,在我们这个示例中最终的结果将是一个区间向量,其中包含两个选定序列在一定时间范围内的样本,但也将包含某些时间步长的序列数据的间隙。