查询基础
Prometheus 提供了一种称为 PromQL(Prometheus 查询语言)的功能性查询语言,允许用户实时选择和聚合时间序列数据。
当您向 Prometheus 发送查询请求时,它可以是即时查询(在某一特定时间点进行评估),也可以是范围查询(在开始和结束时间之间的等距步骤内进行)。PromQL 在这两种情况下的工作方式完全相同;范围查询就像是在不同时间戳上多次运行的即时查询。
在 Prometheus UI 中,“Table”选项卡用于即时查询,“Graph”选项卡用于范围查询。
其他程序可以通过 HTTP API 获取 PromQL 表达式的结果。
示例
本文档是 Prometheus 的基础语言参考。对于初学者,建议先从几个 示例 开始。
样本
PromQL 返回的给定时间戳的样本值可以是浮点数或 原生直方图 (native histogram)。浮点样本是一个简单的浮点数,而原生直方图样本包含完整的直方图,包括计数、总和和存储桶 (buckets)。
请注意,PromQL 文档中的“直方图样本”一词始终指代原生直方图。“经典直方图”一词是指一组包含带有 _bucket、_count 和 _sum 后缀的浮点样本的时间序列,它们共同描述一个直方图。从 PromQL 的角度来看,这些只包含浮点样本,不存在“经典直方图样本”。
浮点样本和直方图样本都可以具有计数器 (counter) 或仪表 (gauge) 的“风味”。具有计数器或仪表风味的浮点样本通常直接被称为“计数器”或“仪表”,而它们的直方图对应物被称为“计数器直方图”或“仪表直方图”。浮点样本不存储其风味,用户在编写 PromQL 查询时需要自行考虑其风味。(按照惯例,包含浮点计数器的时间序列名称以 _total 结尾,以帮助区分。)
由于直方图样本“知道”其计数器或仪表风味,这允许对不匹配的操作发出可靠的警告。例如,将 rate 函数应用于仪表浮点数很可能会产生无意义的结果,但查询会毫无怨言地处理。然而,如果将其应用于仪表直方图,查询结果将被标注警告。
表达式语言数据类型
在 Prometheus 的表达式语言中,一个表达式或子表达式可以求值为以下四种类型之一:
- 即时向量 (Instant vector) - 一组时间序列,其中每个时间序列包含单个样本,且所有样本共享相同的时间戳。
- 范围向量 (Range vector) - 一组时间序列,其中每个时间序列包含一段时间内的数据点范围。
- 标量 (Scalar) - 一个简单的浮点数值。
- 字符串 (String) - 一个简单的字符串值;目前未使用。
根据使用场景(例如绘图与显示表达式输出),只有部分类型可作为用户指定表达式的结果。对于 即时查询,上述任何数据类型都允许作为表达式的根。而 范围查询 仅支持标量类型和即时向量类型的表达式。
向量和时间序列可能同时包含浮点样本和直方图样本。
直方图存储桶布局的协调
原生直方图可以具有不同的存储桶布局,但它们通常可以转换为兼容版本,以便对它们应用二进制和聚合操作。适用于原生直方图的范围向量函数也会执行这种协调。在二进制操作中,这种协调是成对执行的;在聚合操作和函数中,所有直方图样本都被协调为一种兼容的存储桶布局。
并非所有存储桶布局都可以协调,如果在操作中遇到不兼容的直方图,则相应的输出向量元素将从结果中移除,并标记有警告级别的注释。更多详细信息可以在 原生直方图规范 中找到。
字面量
以下部分描述了各种类型的字面值。注意,不存在“直方图字面量”。
字符串字面量
字符串字面量由单引号、双引号或反引号指定。
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
此外,可以在十进制或十六进制数字之间使用下划线 (_) 以提高可读性。
示例
1_000_000
.123_456_789
0x_53_AB_F3_82
浮点字面量也用于指定以秒为单位的时间间隔。为方便起见,十进制整数可以与以下时间单位组合:
ms– 毫秒s– 秒 – 1s 等于 1000msm– 分钟 – 1m 等于 60s(忽略闰秒)h– 小时 – 1h 等于 60md– 天 – 1d 等于 24h(忽略所谓的夏令时)w– 周 – 1w 等于 7dy– 年 – 1y 等于 365d(忽略闰年)
将十进制整数加上上述单位之一,是与纯浮点字面量等效的秒数的另一种表示形式。
示例
1s # Equivalent to 1.
2m # Equivalent to 120.
1ms # Equivalent to 0.001.
-2h # Equivalent to -7200.
以下示例是不起作用的:
0xABm # No suffixing of hexadecimal numbers.
1.5h # Time units cannot be combined with a floating point.
+Infd # No suffixing of ±Inf or NaN.
可以通过连接加后缀的整数来组合多个单位。单位必须按从大到小的顺序排列。给定的单位在一个浮点字面量中只能出现一次。
示例
1h30m # Equivalent to 5400s and thus 5400.
12h34m56s # Equivalent to 45296s and thus 45296.
54s321ms # Equivalent to 54.321.
时间序列选择器
这些是指导 PromQL 获取哪些数据的基本构建块。
即时向量选择器
即时向量选择器允许选择一组时间序列,并为每个时间序列提供给定时间戳(时间点)的单个样本值。在最简单的形式中,只需指定指标名称,这将产生一个即时向量,其中包含所有具有该指标名称的时间序列的元素。
返回的值将是查询评估时间戳(对于 即时查询)或查询中的当前步长(对于 范围查询)处或之前的最新样本值。@ 修饰符 允许覆盖选择发生的参考时间戳。只有当时间序列的最新样本在 回溯周期 之前时,才会返回该时间序列。
此示例选择所有具有 http_requests_total 指标名称的时间序列,并返回每个时间序列的最新样本:
http_requests_total
可以通过在花括号 ({}) 中附加逗号分隔的标签匹配器列表来进一步过滤这些时间序列。
此示例仅选择那些具有 http_requests_total 指标名称,且 job 标签设置为 prometheus 且 group 标签设置为 canary 的时间序列:
http_requests_total{job="prometheus",group="canary"}
还可以对标签值进行否定匹配,或根据正则表达式匹配标签值。以下是现有的标签匹配运算符:
=:选择与提供的字符串完全相等的标签。!=:选择与提供的字符串不相等的标签。=~:选择与提供的字符串进行正则表达式匹配的标签。!~:选择与提供的字符串不进行正则表达式匹配的标签。
正则表达式 匹配是完全锚定的。env=~"foo" 的匹配被视为 env=~"^foo$"。
例如,这会选择 staging、testing 和 development 环境中,且 HTTP 方法不是 GET 的所有 http_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"}
并将排除:
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:.*"}
指标名称不能是关键字 bool、on、ignoring、group_left 和 group_right 之一。以下表达式是非法的:
on{} # Bad!
此限制的变通方法是使用 __name__ 标签:
{__name__="on"} # Good!
范围向量选择器
范围向量字面量的工作方式与即时向量字面量类似,不同之处在于它们从当前即时时间向回选择一定范围的样本。在语法上,在向量选择器末尾的方括号 ([]) 中附加一个 浮点字面量,以指定每个结果范围向量元素应获取多长时间之前的数值。通常,浮点字面量使用带有一个或多个时间单位的语法,例如 [5m]。该范围是一个左开右闭的区间,即时间戳与范围左边界重合的样本被排除在选择之外,而时间戳与范围右边界重合的样本则包含在选择中。
在这个例子中,我们选择了所有具有指标名称 http_requests_total 且 job 标签设置为 prometheus 的时间序列,在过去 5 分钟内记录的所有值:
http_requests_total{job="prometheus"}[5m]
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):
rate(http_requests_total[5m] offset 1w)
当查询过去的样本时,负偏移量将启用未来时间的时间比较:
rate(http_requests_total[5m] offset -1w)
注意,这允许查询查看其评估时间之后的数据。
@ 修饰符
@ 修饰符允许更改查询中单个即时向量和范围向量的评估时间。提供给 @ 修饰符的时间是一个 Unix 时间戳,并用浮点字面量描述。
例如,以下表达式返回 2021-01-04T07:40:00+00:00 时 http_requests_total 的值:
http_requests_total @ 1609746000
请注意,@ 修饰符必须始终紧跟在选择器后面,即以下写法是正确的:
sum(http_requests_total{method="GET"} @ 1609746000) // GOOD.
而以下写法是不正确的:
sum(http_requests_total{method="GET"}) @ 1609746000 // INVALID.
这同样适用于范围向量。它返回 http_requests_total 在 2021-01-04T07:40:00+00:00 时的 5 分钟速率:
rate(http_requests_total[5m] @ 1609746000)
@ 修饰符支持上述所有数字字面量的表示形式。它与 offset 修饰符一起使用时,偏移量是相对于 @ 修饰符时间应用。无论修饰符的顺序如何,结果都是相同的。
例如,这两个查询将产生相同的结果:
# 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 <float_literal> ]
<resolution>是可选的。默认为全局评估间隔。
运算符
Prometheus 支持许多二进制和聚合运算符。详情请参阅 表达式语言运算符 页面。
函数
Prometheus 支持多种函数来操作数据。详情请参阅 表达式语言函数 页面。
注释
PromQL 支持以 # 开头的行注释。例如:
# This is a comment
正则表达式
Prometheus 中的所有正则表达式都使用 RE2 语法 。
正则表达式匹配始终是完全锚定的。
陷阱
陈旧性 (Staleness)
在查询期间,采样数据的时间戳是独立于实际当前时间序列数据进行选择的。这主要是为了支持聚合(sum, avg 等)等情况,其中多个聚合的时间序列在时间上并不精确对齐。由于它们的独立性,Prometheus 需要为每个相关时间序列在这些时间戳处分配一个值。它通过获取小于回溯周期之前的最新样本来实现。回溯周期默认为 5 分钟,但可以 通过 --query.lookback-delta 标志设置,或者通过 lookback_delta 参数在单个查询上进行覆盖。
如果目标抓取或规则评估不再为之前存在的时间序列返回样本,则该时间序列将被标记为陈旧。如果删除了目标,之前检索到的时间序列在删除后不久将被标记为陈旧。
如果在一个时间序列被标记为陈旧之后的采样时间戳处评估查询,则不会为该时间序列返回任何值。如果随后为该时间序列摄取了新样本,它们将按预期返回。
当时间序列不再被导出或目标不再存在时,它将变陈旧。此类时间序列将在其最后收集样本的时间从图表中消失,并且在被标记为陈旧后,它们将不会在查询中返回。
一些在其样本上放置自己时间戳的 Exporter 具有不同的行为:停止导出的序列在消失前会取最后的值(默认 5 分钟)。track_timestamps_staleness 设置可以改变这种行为。
避免缓慢查询和过载
如果查询需要操作大量数据,将其绘图可能会导致服务器或浏览器超时或过载。因此,在构建针对未知数据的查询时,请始终先在 Prometheus 表达式浏览器的表格视图(tabular view)中构建查询,直到结果集看起来合理为止(最多数百条,而非数千条时间序列)。只有在对数据进行了充分的过滤或聚合后,再切换到图形模式。如果表达式在即时绘图时仍然耗时过长,请通过记录规则(recording rule)进行预记录。
这一点对于 Prometheus 的查询语言尤为重要,因为像 api_http_requests_total 这样的裸指标名称选择器可能会扩展为带有不同标签的数千条时间序列。此外,请记住,即使输出的时间序列数量很少,聚合大量时间序列的表达式也会对服务器产生负载。这类似于在关系型数据库中对某一列的所有值求和会很慢,即使输出结果只是一个单一的数字。