4. PromQL 基础:即时向量与范围向量、选择器与标签匹配、基础聚合操作

PromQL(Prometheus Query Language)是 Prometheus 生态中用于数据查询与分析的唯一语言。在 SLO 作业模板与故障自愈场景中,绝大多数告警规则、Dashboard 面板、自动化决策逻辑都依赖 PromQL 的精确表达。本章聚焦 PromQL 最核心的三个概念:向量类型(即时向量 vs 范围向量)、标签选择器(精确匹配、正则匹配、负向匹配)以及基础聚合函数(rate / irate / sum / avg)。掌握这些基础后,你就能读懂并编写 80% 以上的 SLO 相关查询。

4.1 即时向量与范围向量

PromQL 的查询结果分为两种向量类型,理解它们的区别是正确使用聚合函数的前提。

向量类型 含义 时间维度 典型用途
即时向量(Instant Vector) 每个时间序列在当前查询时刻的最新样本值 单点快照 当前 CPU 使用率、当前内存占用、当前请求数
范围向量(Range Vector) 每个时间序列在过去一段时间窗口内的一组样本值 时间窗口 [t-duration, t] 过去 5 分钟的请求速率、过去 1 小时的错误率趋势

在 PromQL 语法中,范围向量通过在指标名后加 [时间窗口] 表示,例如 http_requests_total[5m] 返回过去 5 分钟内每个时间序列的所有样本。即时向量则不加方括号,例如 http_requests_total 只返回最新值。

关键区别: 聚合函数如 rate()、irate()、avg_over_time() 只能作用于范围向量;而 sum()、avg() 等运算符可以作用于即时向量。初学者最常见的错误是将 rate() 直接套用在即时向量上,导致查询报错。
即时向量 vs 范围向量 对比示意图 即时向量(Instant Vector) 查询:http_requests_total 返回:每个 series 的 最新 1 个样本 用途:当前状态快照 范围向量(Range Vector) 查询:http_requests_total[5m] 返回:每个 series 在 5 分钟内的所有样本 用途:计算速率、趋势、分位数 加 [5m] ⚠ 常见误区 • rate(http_requests_total) ❌ 错误:rate() 需要范围向量 • rate(http_requests_total[5m]) ✅ 正确:先取范围向量再计算速率 • sum(http_requests_total) ✅ 正确:sum() 可直接作用于即时向量

4.2 选择器与标签匹配

Prometheus 的数据模型基于指标名 + 标签键值对。选择器(Selector)用于从时间序列数据库中精确筛选目标数据。标签匹配支持四种操作符:

操作符 含义 示例 匹配行为
= 精确相等 job="api-server" 标签值完全等于指定字符串
!= 不相等 job!="api-server" 排除标签值等于指定字符串的 series
=~ 正则匹配 instance=~"10\\.0\\.1\\..*" 标签值匹配正则表达式
!~ 正则不匹配 instance!~"10\\.0\\.1\\..*" 排除标签值匹配正则的 series
实战建议: 在 SLO 场景中,常用 =~ 批量选择一组同类服务(如 service=~"user|order|payment"),用 != 排除健康检查端点(如 path!="/healthz")。正则表达式中的点号 . 需要转义为 \\.,否则会匹配任意字符。

选择器可以组合多个标签条件,用逗号分隔,逻辑为 AND:

# 选择 job 为 api-server,且 method 为 GET 或 POST 的 series
http_requests_total{job="api-server", method=~"GET|POST"}

# 排除健康检查路径,且只保留 production 环境
http_requests_total{path!="/healthz", env="production"}

4.3 基础聚合操作:rate / irate / sum / avg

聚合函数是 PromQL 从原始样本中提取业务语义的核心工具。在 SLO 计算中,rate() 和 irate() 用于将 Counter 类型指标转换为每秒速率;sum() 和 avg() 用于跨维度汇总。

4.3.1 rate() 与 irate():Counter 转速率

函数 计算方式 适用场景 特点
rate() 时间窗口内首尾样本差值 / 时间跨度 长期趋势、SLO 窗口(5m / 30m / 1h) 平滑,抗毛刺,适合告警
irate() 时间窗口内最后两个样本差值 / 时间间隔 实时监控、Dashboard 瞬时速率 灵敏,易受采样间隔影响,不适合告警
# 过去 5 分钟的平均每秒请求数(推荐用于 SLO 告警)
rate(http_requests_total[5m])

# 过去 5 分钟的瞬时每秒请求数(基于最后两个样本)
irate(http_requests_total[5m])
注意: rate() 和 irate() 只能用于 Counter 类型指标(单调递增)。如果用于 Gauge 类型(如内存使用率),结果无意义。另外,时间窗口至少需要包含两个样本点,否则返回 NaN。

4.3.2 sum() 与 avg():跨维度聚合

sum() 对指定维度求和,avg() 计算平均值。两者通常与 by 或 without 子句配合,控制聚合后保留哪些标签。

聚合函数 作用 典型 SLO 用法
sum() 对值求和 计算总请求数、总错误数
avg() 计算平均值 计算平均延迟、平均 CPU 使用率
# 按 job 维度聚合,计算每个 job 的总请求速率
sum by (job) (rate(http_requests_total[5m]))

# 按 service 和 method 维度聚合,计算平均延迟
avg by (service, method) (rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m]))

4.3.3 组合使用:SLO 错误率计算示例

以下是一个典型的 SLO 错误率查询,综合使用了 rate()、sum() 和标签选择器:

# 计算每个 service 过去 1 小时的错误率(HTTP 5xx / 总请求)
(
  sum by (service) (
    rate(http_requests_total{status=~"5.."}[1h])
  )
  /
  sum by (service) (
    rate(http_requests_total[1h])
  )
) * 100

该查询的语义分解:

  1. rate(http_requests_total{status=~"5.."}[1h]) — 取过去 1 小时内所有 5xx 状态码的每秒速率(范围向量 → 即时向量)
  2. sum by (service) (...) — 按 service 标签聚合,将同一 service 下所有实例的错误速率相加
  3. 分母同理计算总请求速率
  4. 两者相除后乘以 100,得到百分比形式的错误率
SLO 错误率查询数据流 原始 Counter http_requests_total {status, service, …} [1h] 范围向量 http_requests_total[1h] 含所有样本点 rate() 每秒速率 rate(...[1h]) 即时向量 sum by(service) 聚合结果 每个 service 一个值 📐 错误率计算公式 错误率 = ( sum by(service) ( rate( {status=~"5.."} [1h] ) ) ) / ( sum by(service) ( rate( [1h] ) ) ) × 100 分子:仅筛选 status 以 5 开头的请求(5xx 错误) 分母:所有请求(不筛选 status) 最终输出:每个 service 一个百分比值,用于 SLO 燃烧速率告警

4.4 本章小结

  • 即时向量返回当前最新值,范围向量返回时间窗口内的样本集合,通过 [时间窗口] 语法区分。
  • 标签选择器支持 =、!=、=~、!~ 四种操作符,组合使用可精确筛选目标时间序列。
  • rate() 计算窗口内平均速率,适合 SLO 告警;irate() 计算瞬时速率,适合实时 Dashboard。
  • sum() 和 avg() 配合 by 子句实现跨维度聚合,是构建 SLO 错误率、延迟等核心指标的基础。
课后练习: 假设你有指标 http_requests_total{status, service, instance},请编写一个 PromQL 查询,计算每个 service 在过去 30 分钟内 status 为 4xx 的请求占比(4xx 错误率)。提示:使用 rate()、sum by (service)、标签选择器 status=~"4.."。