Prometheus PromQL 查询基础:即时查询与范围查询、选择器、时间修饰符与字面量详解
2026/9/7 23:49:30 网站建设 项目流程

Prometheus PromQL 查询基础:即时查询与范围查询、选择器、时间修饰符与字面量详解

【免费下载链接】prometheusThe Prometheus monitoring system and time series database.项目地址: https://gitcode.com/GitHub_Trending/pr/prometheus

本篇技术指南以 Prometheus 官方文档《Querying basics》为主体,系统讲解 PromQL(Prometheus Query Language)的数据类型、字面量、时间序列选择器、offset@时间修饰符、子查询以及查询引擎的时间语义。读完本文,你将能够正确编写即时查询与范围查询、理解 lookback/staleness 机制的底层行为,并结合开源仓库中的解析器与引擎源码,定位查询行为背后的实现依据。

两种查询形态:即时查询与范围查询

Prometheus 提供了一套函数式查询语言 PromQL,让用户能够实时地选择与聚合时间序列数据。向 Prometheus 发送查询请求时,请求可以是:

  • 即时查询(instant query):在单一时间点求值;
  • 范围查询(range query):在起止时间之间以固定步长(step)间隔多次求值。

两种情形下 PromQL 的工作方式完全一致:范围查询本质上就是在不同时间戳上多次执行的即时查询。

在 Prometheus Web UI 中,"Table" 页签对应即时查询,"Graph" 页签对应范围查询。其他程序则可以通过 HTTP API 获取任意 PromQL 表达式的求值结果。

样本(Samples):浮点样本与原生直方图样本

PromQL 在某个时间戳返回的样本值,可能是浮点数(float),也可能是**原生直方图(native histogram)**样本:

  • 浮点样本就是一个简单的浮点数;
  • 原生直方图样本则包含一个完整的直方图,包括 count、sum 和各个桶(buckets)。

一个容易混淆的术语约定:

术语含义
histogram sample(直方图样本)在 PromQL 文档语境下,永远指原生直方图
classic histogram(经典直方图)_bucket_count_sum三个后缀的一系列时间序列组成,共同描述一个直方图

从 PromQL 的视角看,经典直方图里只有浮点样本,并不存在“经典直方图样本”这一说法。

浮点样本与直方图样本都可以具有 counter 或 gauge 的“flavor”(语义类型):

  • 浮点样本不存储自身的 flavor,由写查询的人自行注意(约定上,包含浮点 counter 的时间序列名以_total结尾以帮助区分);
  • 直方图样本“知道”自己是 counter 还是 gauge,因此引擎可以对不匹配的操作给出可靠告警。例如对 gauge 浮点序列使用rate()函数,查询不会报错但结果很可能无意义;而对 gauge 直方图执行同样的操作,查询结果会带有警告注解(warning annotation)。

此外,向量(vector)和时间序列可以同时混合包含浮点样本与直方图样本。

表达式语言的数据类型

在 Prometheus 表达式语言中,一个表达式或子表达式可以求值为四种类型之一:

  1. Instant vector(即时向量)——一组时间序列的集合,每个时间序列包含一个样本,所有样本共享同一时间戳;
  2. Range vector(范围向量)——每个时间序列包含一段时间内的多个数据点;
  3. Scalar(标量)——简单的数值型浮点值;
  4. String(字符串)——简单的字符串值;当前尚未使用。

不同类型的合法性因使用场景而异:对于即时查询,上述任意数据类型都可以作为表达式根节点的结果;而范围查询只支持标量类型(scalar)和即时向量类型(instant vector)的表达式。

从源码结构看,这四种类型在 类型定义 中以ValueType常量(vectorscalarmatrixstring)表达,DocumentedType()函数把内部类型名映射为文档面向用户的术语(如vector→ "instant vector"、matrix→ "range vector"),可见文档术语与解析器内部术语是一一对应的。

原生直方图桶布局的对齐(Reconciliation)

原生直方图可能具有不同的桶布局(bucket layout),但在执行二元运算和聚合运算前,通常可以被转换为兼容的版本。作用于范围向量、且适用于原生直方图的函数同样会执行这种对齐:

  • 二元运算中,对齐是成对(pairwise)进行的;
  • 聚合运算与聚合函数中,所有直方图样本会被对齐到同一个兼容的桶布局。

并非所有桶布局都能被对齐。若操作中遇到不兼容的直方图,对应的输出向量元素会从结果中移除,并带上 warn 级别的注解(annotation)。

字面量(Literals)

PromQL 中不存在“直方图字面量”。以下是各类字面量的语法说明。

字符串字面量

字符串字面量由单引号、双引号或反引号界定。单引号与双引号字符串遵循与 Go 相同的转义规则:反斜杠开始转义序列,后跟abfnrtv\;也可用八进制(\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 = 1000ms
m分钟,1m = 60s(忽略闰秒)
h小时,1h = 60m
d天,1d = 24h(忽略夏令时)
w周,1w = 7d
y年,1y = 365d(忽略闰日)

给十进制整数加上单位,等价于以纯浮点字面量表示相同秒数:

1s # 等价于 1 2m # 等价于 120 1ms # 等价于 0.001 -2h # 等价于 -7200

以下写法不合法

0xABm # 十六进制数不允许加单位后缀 1.5h # 时间单位不能与浮点数组合 +Infd # ±Inf 与 NaN 不允许加单位后缀

多个单位可以拼接,但必须从大到小排列,且同一单位在一个字面量中只能出现一次:

1h30m # 等价于 5400s,即 5400 12h34m56s # 等价于 45296s,即 45296 54s321ms # 等价于 54.321

持续时间表达式(Duration expressions)

在需要时间持续时间的地方——即范围向量选择器offset持续时间——都可以使用算术表达式。

在范围向量中:

rate(http_requests_total[5m * 2]) # 10 分钟范围 rate(http_requests_total[(5+2) * 1m]) # 7 分钟范围

在 offset 持续时间中:

http_requests_total offset (1h / 2) # 30 分钟偏移 http_requests_total offset ((2 ^ 3) * 1m) # 8 分钟偏移

使用offset加持续时间表达式时,必须用括号包裹整个表达式;不加括号的话,offset 计算只会取第一个持续时间值。

支持的运算符遵循常规优先级规则:

运算符含义
+加法
-减法
*乘法
/除法
%取模
^幂运算

持续时间表达式中还可使用以下函数:

  • step():解析为范围查询的步长(step width);即时查询中解析为0s
  • range():解析为范围查询的总时长(end time − start time);即时查询中解析为0s。与@ end()结合尤其有用,例如max_over_time(metric[range()] @ end())可以在整个查询范围内回溯;
  • min_of(<duration>, <duration>):返回两者中较小者,用于给持续时间设置上限;
  • max_of(<duration>, <duration>):返回两者中较大者,用于强制最小值。

例如,max_of(step(), 5s)保证持续时间不小于5smin_of(range(), 1h)把持续时间上限封顶在1h

注意@修饰符中不支持持续时间表达式。

等价持续时间示例:

  • 5m * 2等价于10m600s
  • 10m - 1m等价于9m540s
  • (5+2) * 1m等价于7m420s
  • 1h / 2等价于30m1800s
  • 4h % 3h等价于1h3600s
  • (2 ^ 3) * 1m等价于8m480s
  • step() + 1等价于查询步长增加1s
  • max_of(step(), 5s)等价于查询步长与5s中的较大者;
  • min_of(2 * step() + 5s, 5m)等价于“查询步长两倍加5s”与5m中的较小者。

从源码看,持续时间表达式在解析后并不立即求值,而是以 AST 节点DurationExpr形式挂在OriginalOffsetExpr/RangeExpr/StepExpr字段上(见 AST 定义),在执行前由 durationVisitor 统一计算。该访问器处理三类节点:VectorSelector(offset 表达式)、MatrixSelector(范围表达式)和SubqueryExpr(offset、step、range 三类表达式)。求值过程还包含重要的健壮性检查(见 calculateDuration):

  • 显式拒绝 NaN 与 ±Inf,防止其绕过边界检查后在time.Duration转换时产生未定义值;
  • 拒绝超出 int64 纳秒表示范围的持续时间(即 |seconds| 不超过 2^63/1e9);
  • 除零、取模零会直接报错;
  • 范围向量与子查询的范围/步长不允许为负或零,而 offset 允许负值(用于时间前向比较)。

时间序列选择器

选择器是 PromQL 的基本构件,指明查询引擎要获取哪些数据。

即时向量选择器

即时向量选择器在一给定时间戳处选择一组时间序列,并为每个序列返回一个样本值。最简单的形式只写指标名,结果是一个即时向量,包含所有该指标名的时间序列:

http_requests_total

返回的值是查询求值时间戳之前最近的那个样本(即时查询对应查询时间,范围查询对应当前 step)。使用@修饰符 可以覆盖选择所依据的时间戳。只有当时间序列的最近样本距当前时刻不超过 lookback period 时,该序列才会被返回。

可以用花括号{}追加逗号分隔的标签匹配器进一步过滤:

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

标签匹配操作符共四个:

操作符含义
=与给定字符串完全相等
!=与给定字符串不相等
=~与给定字符串正则匹配
!~与给定字符串正则不匹配

正则匹配是完全锚定的:env=~"foo"等价于env=~"^foo$"

例如,选择stagingtestingdevelopment三种环境下、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=""}会匹配并返回前三条(它们都没有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!

从源码看,这条规则由解析器在 validate 阶段强制执行:“A Vector selector must contain at least one non-empty matcher to prevent implicit selection of all metrics (e.g. by a typo)”——防止因拼写错误而隐式选中全部指标。

标签匹配器也可以作用于指标名本身,通过匹配内部__name__标签实现。例如http_requests_total等价于{__name__="http_requests_total"},且!==~!~也可使用。以下表达式选中所有名字以job:开头的指标:

{__name__=~"job:.*"}

指标名不得是boolonignoringgroup_leftgroup_right这些关键字之一。以下表达式非法:

on{} # Bad!

变通方案是借助__name__标签:

{__name__="on"} # Good!

范围向量选择器

范围向量字面量与即时向量字面量工作方式相同,但选择的是从当前时刻向前回溯一段时间内的样本序列。语法上,在向量选择器末尾用方括号[]追加一个浮点字面量,指定向前取多少秒的数据。常见写法使用带时间单位的字面量,如[5m]

范围区间是左开右闭的:时间戳恰好在左边界上的样本被排除,时间戳恰好在右边界上的样本被包含。

示例——选择所有指标名为http_requests_totaljob标签为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(http_requests_total[5m] offset 1w)

查询历史样本时,负数 offset可以实现向未来方向的时间比较:

rate(http_requests_total[5m] offset -1w)

注意:这允许查询“超前于”其求值时间向前看。

@ 修饰符

@修饰符可以改变查询中单个即时向量或范围向量的求值时间。@修饰符接受的时间值是用浮点字面量表示的Unix 时间戳

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

http_requests_total @ 1609746000

offset相同,@必须紧跟选择器。这样写是对的:

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

而下面这种是错误的:

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

对范围向量同理,返回2021-01-04T07:40:00+00:00时刻的 5 分钟速率:

rate(http_requests_total[5m] @ 1609746000)

@修饰符支持上文描述的全部数字字面量形式。它还可以与offset修饰符一起使用,此时 offset 相对于@修饰符指定的时间生效;无论两个修饰符的书写顺序如何,结果相同:

# offset 在 @ 之后 http_requests_total @ 1609746000 offset 5m # offset 在 @ 之前 http_requests_total offset 5m @ 1609746000

此外,start()end()可作为@修饰符的特殊取值:

  • 范围查询,二者分别解析为该范围查询的起始与结束时间,且对所有 step 保持不变;
  • 即时查询start()end()都解析为求值时间。
http_requests_total @ start() rate(http_requests_total[5m] @ end())

注意:@修饰符允许查询超前于其求值时间。

从源码结构看,@修饰符在解析器中以Timestamp *int64字段(绝对时间戳)和StartOrEnd ItemType字段(标记start()/end())承载于 VectorSelector 与 SubqueryExpr 上;Offset字段则存放执行时实际使用的偏移量,由原始 offset、@时间、求值时间与子查询偏移共同计算得出。

子查询(Subquery)

子查询允许你针对给定的时间范围与分辨率运行一次即时查询。子查询的结果是一个范围向量。

语法:

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

其中<resolution>(分辨率,即子查询内部的 step)是可选的,默认取全局求值间隔。

从 AST 结构看,SubqueryExpr 同时携带Range/RangeExpr(范围)、Step/StepExpr(分辨率)与OriginalOffset/OriginalOffsetExpr(偏移),三者均可使用持续时间表达式,并在 durationVisitor 中统一求值。

运算符与函数

Prometheus 支持大量二元运算符与聚合运算符,详见表达式语言运算符页;也支持若干对数据操作的函数,详见表达式语言函数页。

注释

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

# This is a comment

正则表达式

Prometheus 中所有正则表达式均使用 RE2 语法(Google RE2,具有线性时间复杂度的确定性匹配特性),且正则匹配始终是全程锚定的。

常见陷阱(Gotchas)

Staleness(陈旧样本)与 lookback period

查询采样所用的时间戳独立于实际现存时间序列数据选择,这主要是为了支持聚合(sumavg等)这类场景——被聚合的多个时间序列在时间上往往并不精确对齐。正因其独立性,Prometheus 必须为每个相关时间序列在这些时间戳上分配一个值:它的做法是取“距该时间戳不超过 lookback period 之前的最新样本”

lookback period 默认 5 分钟,可以通过--query.lookback-delta命令行标志设置(见命令行参考),也可以在单个查询中通过lookback_delta参数覆盖。

从源码看,默认值定义于 engine.go 的defaultLookbackDelta = 5 * time.Minute;当LookbackDelta为 0 时引擎自动回退到该默认值;命令行标志在 main.go 中注册为query.lookback-delta(描述为 “The maximum lookback duration for retrieving metrics during expression evaluations and federation.”)。行为边界由 TestQueryLookbackDelta 测试用例验证:样本恰好落后一个默认 lookback(5 分钟)时会被查询取到,再落后一个毫秒则不再取到。

陈旧(stale)语义

  • 当某个 target 的抓取或规则求值不再返回先前存在的时间序列样本时,该序列会被标记为 stale;
  • 当 target 被移除时,其先前抓取的时间序列也会很快被标记为 stale;
  • 若查询的采样时间戳晚于序列被标记为 stale 的时刻,则不再为该序列返回值;之后若重新摄入新样本,则按预期返回;
  • 序列在不再导出、或 target 不存在时变 stale,它们会从图表中最后采集样本的时刻消失,之后查询不再返回。

一个例外:某些自行打时间戳的 exporter 行为不同——其停止导出的序列会在消失前保持最后一个值(默认 5 分钟)。track_timestamps_staleness配置可以改变该行为。

避免慢查询与过载

当查询需要处理大量数据时,绘图可能会超时,甚至压垮服务器或浏览器。因此,在未知数据上构建查询时,始终先在 Prometheus 表达式浏览器的表格视图(tabular view)中构建查询,直到结果集看起来合理(最多几百条、而非几千条时间序列)。只有当数据被充分过滤或聚合后,再切换到图表模式。如果表达式即席(ad-hoc)绘图仍然太慢,就通过记录规则预先记录计算结果。

这一点在 Prometheus 中尤其关键:像api_http_requests_total这样裸的指标名选择器,可能扩展为数千条带不同标签的时间序列。同时要记住,对大量时间序列做聚合的表达式即使输出只有少数几条序列,也会给服务器带来负载——正如在关系数据库中对某一整列求和,即便输出只是一个数字,也会很慢。

小结与延伸阅读

主题关键要点
即时/范围查询范围查询 = 不同时间戳上的多次即时查询;Table 页签即时,Graph 页签范围
样本类型浮点样本与原生直方图样本;直方图自带 counter/gauge 语义,可产生可靠性告警
四种数据类型instant vector、range vector、scalar、string(未用);范围查询仅支持 scalar 与 instant vector
字面量字符串(三种引号)、浮点(含下划线、十六进制)、时间单位(ms/s/m/h/d/w/y 从大到小拼接)
持续时间表达式+ - * / % ^step()range()min_of()max_of();offset 表达式需加括号;@中不支持
选择器空值匹配器选中无该标签的序列;{job=~".*"}非法,至少需一个非空匹配器
offset/@均须紧跟选择器;支持负 offset 与start()/end(),可超前求值时间
Staleness默认 5 分钟 lookback,--query.lookback-delta或单查询lookback_delta可调

更多学习材料可参考仓库中的 示例文档(建议初学者先通过示例入门)、运算符、函数与 HTTP API 参考页;深入实现可阅读 promql/parser/parse.go(解析与校验)、promql/parser/ast.go(AST 节点定义)、promql/durations.go(持续时间表达式求值)与 promql/engine.go(查询引擎)。

【免费下载链接】prometheusThe Prometheus monitoring system and time series database.项目地址: https://gitcode.com/GitHub_Trending/pr/prometheus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询