Prometheus 提供了一种名为 PromQL (Prometheus Query Language) 的功能性查询语言,允许用户实时选择和聚合时间序列数据。表达式的结果既可以显示为图形,也可以在 Prometheus 的表达式浏览器中显示为表格数据,或者被外部系统通过 HTTP API 使用。

查询示例

本文档是 Prometheus 的基础语法参考资料。从几个例子开始可能会更容易学习。

简单的时间序列选择

返回指标http_requests_total的所有时间序列:

http_requests_total

返回指标 http_requests_total 包含给定的joblabel的所有的时间序列:

http_requests_total{job="apiserver", handler="/api/comments"}

返回同一个向量的整个时间范围(在本例中为查询时间之前的5分钟) ,使其成为范围向量:

http_requests_total{job="apiserver", handler="/api/comments"}[5m]

请注意,生成范围向量的表达式不能直接绘制图表,而是在表达式浏览器的表格(“Console”)视图中查看。

使用正则表达式,可以选择 job 与特定模式匹配的时间序列,在本例中,所有以server结尾的job

http_requests_total{job=~".*server"}

Prometheus中的所有正则表达式都使用 RE2 语法。

要选择除4xx之外的所有HTTP状态码,可以执行:

http_requests_total{status!~"4.."}

子查询

返回过去30分钟http_requests_total指标的5分钟速率,分辨率为1分钟。

rate(http_requests_total[5m])[30m:1m]

这是一个嵌套子查询的示例。对 deriv 函数的子查询使用默认的解析度。请注意,不必要地使用子查询是不明智的。

max_over_time(deriv(rate(distance_covered_total[5s])[30s:5s])[10m:])

使用函数、运算符等

返回http_requests_total时间序列过去5分钟内的每秒速率:

rate(http_requests_total[5m])

假设http_requests_total时间序列都有jobinstance 标签,如果想对所有实例的速率求和,这样我们得到的输出时间序列更少,但仍然保留了job维度:

sum by (job) (
  rate(http_requests_total[5m])
) 

如果我们有两个具有相同维度标签的不同度量,我们可以对它们应用二元运算符,两边具有相同标签集的元素将被匹配并传播到输出中。例如,该表达式会返回每个实例的未使用内存(单位:MiB)(在一个虚构的集群调度器上,该集群调度器会公开其运行的实例的这些指标):

(instance_memory_limit_bytes - instance_memory_usage_bytes) / 1024 / 1024

同样的表达式,但是通过app求和,可以这样写:

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)

表达式数据类型

当 Prometheus 采集到监控指标样本数据后,我们就可以通过 PromQL 对监控样本数据进行查询。基本的 Prometheus 查询的结构非常类似于一个 metric 指标,以指标名称开始。

在 Prometheus 的表达式语言中,表达式或子表达式可评估为四种类型之一:

  • 瞬时向量 - 一组时间序列,包含每个时间序列的单个样本,所有样本共享相同的时间戳
  • 范围向量 - 一组时间序列,每个时间序列都包含一定时间范围的数据点
  • 标量 - 一个简单的浮点数值
  • 字符串 - 一个简单的字符串值;目前未使用

根据不同的使用场景(如绘制图形与显示表达式的输出),只有其中某些类型可以作为用户指定表达式的结果。例如,返回瞬时向量的表达式是唯一可以绘制图形的类型。

字面量

字符串字面量

字符串字面用单引号、双引号或反引号表示。

PromQL 遵循与 Go 相同的转义规则。对于单引号或双引号中的字符串字面量,反斜杠是转义序列的开头,后面可以跟 a, b, f, n, r, t, v\。特殊字符可以使用八进制(\nnn)或者十六进制(\xnn\unnnn\Unnnnnnnn)。

相反,转义字符不会在用反引号指定的字符串字面量中被解析。值得注意的是,与 Go 不同,Prometheus 不会对反引号内的换行符进行转义。

例如:

"this is a string"
'these are unescaped: \n \\ \t'
`these are not unescaped: \n ' " \t`

浮点字面量

标量浮点数值可按字面整数或浮点数格式书写(为提高可读性,只包含空格):

[-+]?(
      [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

时间序列选择器

时间序列选择器负责选择时间序列和原始或推断的样本时间戳和值。

时间序列选择器不能与可执行时间序列选择器的即时查询和范围查询等更高层次的概念混淆。更高级别的即时查询会在一个时间点对给定的选择器进行评估,而范围查询则会在最小和最大时间戳之间的多个不同时间点对选择器进行评估。

瞬时向量选择器(Instant vector selectors)

瞬时向量选择器允许选择一组时间序列和每个时间序列在给定时间戳(时间点)上的单个样本值。最简单的情况下,只需指定一个度量名称,就能得到一个包含有该度量名称的所有时间序列元素的瞬时矢量。

比如,下面的示例选择具有 http_requests_total 度量名称的所有时间序列:

http_requests_total

通过在花括号({})中附加逗号分隔的标签匹配器列表,可以进一步筛选这些时间序列。

比如,下面的示例选择指标名称为http_requests_total,且job 标签值为 prometheusgroup 标签值为 canary 的时间序列:

http_requests_total{job="prometheus",group="canary"}

也可以对标签值进行不等于匹配,或根据正则表达式匹配标签值。支持以下标签匹配操作符:

  • = : 选择与提供的字符串完全相同的标签。
  • != : 选择与提供的字符串不相同的标签。
  • =~ : 选择与提供的正则表达式字符串相匹配的标签。
  • !~ : 选择与提供的正则表达式字符串不匹配的标签。

正则表达式的匹配模式是完全匹配。env=~"foo"的匹配被视为 env=~"^foo$"

举个例子,下面的表达式会选择所有environmentstagingtesting、和development,且method不是GEThttp_requests_total时间序列。

http_requests_total{environment=~"staging|testing|development",method!="GET"}

匹配空标签值的标签匹配器也会选择没有配置这个标签的所有时间序列。同一标签名称可以有多个匹配器。

例如,对于以下数据集:

http_requests_total
http_requests_total{replica="rep-a"}
http_requests_total{replica="rep-b"}
http_requests_total{environment="development"}

http_requests_total{environment=""} 会返回以下内容。

http_requests_total
http_requests_total{replica="rep-a"}
http_requests_total{replica="rep-b"}

即使这些数据并没有配置environment这个标签也会被environment=""匹配。

并且被以下查询排除。

http_requests_total{environment="development"}

同一标签名称可以使用多个匹配器;它们都必须通过才能返回结果。

下面的查询表达式:

http_requests_total{replica!="rep-a",replica=~"rep.*"}

会匹配以下数据:

http_requests_total{replica="rep-b"}

向量选择器必须指定名称或至少一个与空字符串不匹配的标签匹配器。以下表达式是非法的:

{job=~".*"} # Bad!

相比之下,这些表达式是有效的,因为它们都有一个与空标签值不匹配的选择器。

{job=~".+"}              # Good!
{job=~".*",method="get"} # Good!

标签匹配器也可以通过与内部__name__标签进行匹配来应用于指标名称。例如,表达式http_requests_total等价于{__name__=“http_requests-total”}。也可以使用除=!==~!~)之外的匹配项。以下表达式选择名称以job开头的所有指标:

{__name__=~"job:.*"}

Metric 名称不能是关键字boolonignoringgroup_leftgroup_right之一。以下表达式不合法:

on{} # Bad!

解决此限制的方法是使用 __name__ label:

{__name__="on"} # Good!

范围向量选择器

范围向量字面量的工作原理与即时向量字面量类似,只不过它们选择的是从当前即时开始的采样范围。语法上,在向量选择符的末尾用方括号([])添加一个时间长度,以指定为每个结果范围向量元素获取多远的时间值。范围是一个封闭的区间,即时间戳与范围任一边界重合的样本仍包含在选择范围内。

在这个例子中,选择过去5分钟内所有指标名称为http_requests_totaljob标签为prometheus的时间序列记录的值:

http_requests_total{job="prometheus"}[5m]

时间范围

时间范围通过数字来表示,单位可以使用以下其中之一的时间单位:

  • ms - 毫秒
  • s - 秒
  • m - 分钟
  • h - 小时
  • d - 天
  • w - 周
  • y - 年

忽略闰年和润秒。

支持通过连接进行组合。单位的顺序必须从最长到最短。一个给定的单位在一段时间内只能出现一次。

下面是一些有效值的示例:

5h
1h30m
5m
10s

例如:api_response_status_count[5m] 表示最近5分钟的数据。

Offset 修饰符

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)

@ 修饰符

@修饰符允许更改查询中单个瞬时向量和范围向量的计算时间。提供给@ 修饰符的时间是一个unix时间戳,用浮点数表示。

例如,以下表达式返回 2021-01-04T07:40:00+00:00http_requests_total 的值:

http_requests_total @ 1609746000

@修饰符总是需要紧跟在选择器后面,也就是说,以下内容是正确的:

sum(http_requests_total{method="GET"} @ 1609746000) // GOOD.

但下列内容是不正确的:

sum(http_requests_total{method="GET"}) @ 1609746000 // INVALID.

范围向量也可以使用@修饰符。下面的表达式将返回http_requests_total2021-01-04T07:40:00+00:00时的5分钟速率:

rate(http_requests_total[5m] @ 1609746000)

@ 修饰符支持上述数字字面的所有表示方法。它与偏移修饰符配合使用,偏移量相对于 @ 修饰符的时间使用。无论修饰符的顺序如何,结果都是一样的。

例如,这两个查询将产生相同的结果:

# 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())

子查询

子查询允许你针对给定的范围和解析度运行即时查询。子查询的结果是一个范围向量。

语法:<instant_query> '[' <range> ':' [<resolution>] ']' [ @ <float_literal> ] [ offset <duration> ]

  • <resolution> 是可选的,默认值是全局计算间隔。

操作符

Prometheus 支持许多二元和聚合运算符。

👉 prometheus 运算符

内置函数

待补充 FUNCTIONS

注释

PromQL 支持以 # 开头的行注释。示例:

# This is a comment

缺陷

稳定性

在查询过程中,对数据进行采样的时间戳的选择与当前的实际时间序列数据无关。这主要是为了支持聚合(求和、求平均值等)等情况,其中多个聚合时间序列在时间上并不精确一致。由于它们的独立性,Prometheus 需要在这些时间戳为每个相关时间序列赋值。为此,Prometheus 需要在回溯周期内取该时间戳前的最新样本。默认情况下,回溯周期为 5 分钟。

如果目标扫描或规则评估不再返回先前存在的时间序列样本,则该时间序列将被标记为过时。如果目标被移除,先前检索的时间序列将在移除后不久被标记为过时。

如果在时间序列被标记为过期后才对采样时间戳进行查询评估,则不会返回该时间序列的值。如果随后为该时间序列采集了新样本,则会按预期返回。

当不再导出时间序列或目标不再存在时,该时间序列将变为过期。这些时间序列将在最近一次采集样本时从图表中消失,并且在标记为过期后将不会在查询中返回。

有些导出程序会在样本上加上自己的时间戳,因此会有不同的行为:停止导出的序列在消失前会保留(默认情况下)5 分钟的最后值。track_timestamps_staleness 设置可以改变这种情况。

避免缓慢查询和过载

如果查询需要对大量数据进行操作,则绘制图表可能会超时或使服务器或浏览器超载。因此,在对未知数据进行查询时,总是在 Prometheus 表达式浏览器的表格视图中开始建立查询,直到结果集看起来合理(最多有数百个而不是数千个时间序列)。只有在充分过滤或聚合数据后,才能切换到图形模式。如果表达式绘制临时图表所需的时间仍然太长,可以通过记录规则预先记录。

这与 Prometheus 的查询语言尤其相关,因为像 api_http_requests_total 这样的裸指标名称选择器可能会扩展到数千个具有不同标签的时间序列。此外,请记住,即使输出的只是少量时间序列,汇总多个时间序列的表达式也会对服务器产生负载。这类似于在关系数据库中对一列的所有值求和,即使输出值只有一个数字,也会很慢。

官方文档原文

https://prometheus.io/docs/prometheus/latest/querying/basics/

https://prometheus.io/docs/prometheus/latest/querying/examples/


扫码关注微信公众号