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