4. PromQL 基础:即时查询与范围查询、选择器、偏移量、聚合操作符

PromQL(Prometheus Query Language)是 Prometheus 生态的核心查询语言,用于对时序数据进行筛选、聚合与计算。本章聚焦 PromQL 的四大基础能力:查询类型(即时 vs 范围)、选择器(精确/正则/动态)、偏移量(时间回溯)以及聚合操作符(分组降维)。掌握这些基础是后续编写复杂告警规则与性能面板的前提。

学习提示: 建议在 Prometheus 的 Graph 页面或 Grafana Explore 中同步练习每条查询,观察返回结果的时间范围与标签变化。

4.1 即时查询与范围查询

PromQL 根据查询返回的时间跨度分为两种模式:Instant Query(即时查询) 和 Range Query(范围查询)。两者的区别决定了查询结果的数据结构与使用场景。

特性 即时查询 范围查询
语法示例 node_cpu_seconds_total node_cpu_seconds_total[5m]
返回结果 每个时间序列的一个最新样本值 每个时间序列在时间窗口内的一组样本(矩阵)
时间参数 依赖查询时刻(默认当前时间) 依赖查询时刻 + 窗口长度(如 5m、1h)
典型用途 告警规则、仪表盘当前值、Gauge 瞬时值 速率计算(rate/irate)、分位数、趋势分析
数据量 小(单点) 大(窗口内所有点)
关键区别: 即时查询返回 Instant Vector(瞬时向量),范围查询返回 Range Vector(范围向量)。范围向量不能直接用于算术运算,必须先通过 rate、avg_over_time 等函数转换为瞬时向量。
# 即时查询:返回当前 CPU 空闲百分比
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)) * 100

# 范围查询:返回过去 5 分钟每个 CPU 样本(用于调试)
node_cpu_seconds_total{mode="idle", instance="localhost:9100"}[5m]

4.2 选择器:精确匹配、正则匹配与动态标签

选择器(Selector)用于从指标中筛选特定时间序列。PromQL 支持三种匹配模式:精确匹配、正则匹配 和 负向匹配。选择器写在花括号 {} 内,多个条件用逗号分隔。

匹配类型 操作符 示例 说明
精确相等 = job="api-server" 标签值完全等于指定字符串
精确不等 != job!="api-server" 排除指定标签值
正则匹配 =~ instance=~"10\\.0\\.1\\..*" 标签值匹配正则表达式(RE2 语法)
正则排除 !~ instance!~"10\\.0\\.1\\..*" 标签值不匹配正则表达式
正则陷阱: PromQL 使用 RE2 正则引擎,不支持回溯引用(backreference)和零宽断言(lookahead/lookbehind)。匹配 IP 地址时,点号 . 必须转义为 \\.,否则会匹配任意字符。
# 精确匹配:只查询 production 环境的 HTTP 请求
http_requests_total{job="api-server", environment="production"}

# 正则匹配:查询所有以 "web-" 开头的 job
http_requests_total{job=~"web-.*"}

# 负向正则:排除所有 staging 和 dev 实例
http_requests_total{instance!~".*(staging|dev).*"}

4.3 偏移量:回溯历史数据

偏移量(Offset)允许查询相对于当前时间 过去某个时刻 的数据,常用于对比当前值与历史基线。偏移量使用 offset 关键字,后跟时间单位(s、m、h、d、w、y)。

# 查询 1 小时前的 CPU 使用率
avg(rate(node_cpu_seconds_total{mode="idle"}[5m] offset 1h)) by (instance)

# 对比当前与 24 小时前的内存使用量
node_memory_MemTotal_bytes - node_memory_MemFree_bytes
  -
(node_memory_MemTotal_bytes offset 1d - node_memory_MemFree_bytes offset 1d)
偏移量场景 示例 用途
同比对比 offset 1w 本周流量 vs 上周同期
环比对比 offset 1d 今天错误率 vs 昨天
基线校准 offset 30m 当前延迟 vs 半小时前基线
延迟数据补偿 offset 5m 等待数据采集延迟后对齐时间戳
偏移量限制: offset 不能与 @ 修饰符(指定时间戳)同时使用。偏移量只能向后偏移,不能查询未来数据。

4.4 聚合操作符:分组与降维

聚合操作符(Aggregation Operators)用于将多个时间序列合并为更少的序列,通常配合 by 或 without 子句进行分组。PromQL 提供 11 个聚合操作符,最常用的 6 个如下:

操作符 功能 示例 输出
sum 求和 sum by (job) (http_requests_total) 每个 job 的总请求数
avg 平均值 avg by (instance) (node_cpu_seconds_total) 每个实例的平均 CPU 时间
min 最小值 min by (datacenter) (disk_free_bytes) 每个数据中心的最小磁盘剩余
max 最大值 max by (region) (http_request_duration_seconds) 每个区域的最大请求延迟
count 计数 count by (status) (http_requests_total) 每个状态码的序列数量
topk Top K topk(3, node_memory_MemFree_bytes) 内存剩余最多的 3 个实例
# 按 job 分组求和,忽略 instance 标签
sum by (job) (http_requests_total{status="500"})

# 按 instance 分组求平均,保留所有标签
avg without (cpu) (node_cpu_seconds_total{mode="idle"})

# 取错误率最高的 5 个实例
topk(5, rate(http_requests_total{status="500"}[5m]))
by vs without: by (label) 表示只保留指定标签进行分组;without (label) 表示排除指定标签,保留其余所有标签。通常 by 更直观,推荐优先使用。
PromQL 查询类型与选择器流程 PromQL 查询类型与选择器流程 PromQL 查询 即时查询 (Instant) 范围查询 (Range) Instant Vector 单样本 / 序列 Range Vector 多样本矩阵 选择器 { } 精确匹配 = / != 正则匹配 =~ / !~ 偏移量 offset

4.5 聚合操作符实战:分组与 TopK

聚合操作符最常见的场景是 按标签分组降维 和 异常值定位。以下示例展示如何用 sum by 和 topk 快速发现高负载实例。

# 场景1:按数据中心汇总 CPU 使用率
sum by (datacenter) (
  avg by (instance, datacenter) (
    rate(node_cpu_seconds_total{mode="idle"}[5m])
  )
) * 100

# 场景2:找出内存使用最多的 5 个容器(cAdvisor 指标)
topk(5, container_memory_usage_bytes{container!=""})

# 场景3:计算每个服务的错误率占比
sum by (service) (
  rate(http_requests_total{status=~"5.."}[5m])
) / sum by (service) (
  rate(http_requests_total[5m])
) * 100
聚合顺序陷阱: 先使用 rate 或 avg_over_time 将范围向量转为瞬时向量,再进行聚合。不要对范围向量直接使用 sum,否则会得到错误结果。
聚合操作符分组降维示意 聚合操作符分组降维示意 输入:多标签序列 instance="web-1" instance="web-2" instance="db-1" instance="db-2" sum by (job) 聚合操作符 输出:按 job 降维 job="web" (sum) job="db" (sum) 聚合效果:4 条序列 → 2 条序列 instance 标签被丢弃,job 标签保留,值按 job 求和

4.6 常见错误与避坑指南

  • 范围向量直接运算: 对 [5m] 结果使用 +、* 等算术操作符会报错,必须先通过 rate、avg_over_time 等函数转换。
  • 标签匹配歧义: 正则表达式中的点号 . 匹配任意字符,匹配 IP 时必须转义为 \\.。
  • 偏移量过大: 偏移量超过 Prometheus 数据保留时间(如 offset 90d 但保留 30 天)会返回空结果。
  • 聚合后标签丢失: 使用 sum 未指定 by 时,所有标签被丢弃,结果只有一个值。务必明确分组维度。
  • TopK 与告警: topk 返回的序列数量不固定,不适合直接用于告警规则(告警需要稳定的序列)。
最佳实践: 编写 PromQL 时遵循“先范围转换 → 再过滤 → 再聚合 → 最后运算”的顺序。每步用 by 明确分组标签,避免意外降维。