PromQL 查询基础:语法速览与正确使用

PromQL(Prometheus Query Language)是 Golden Signals 指标查询的核心语言。错误的查询语法不仅导致数据偏差,还会引发高并发下的性能雪崩。本节从向量选择器、范围向量、聚合操作符三个维度,梳理正确用法与常见陷阱。

1. 向量选择器:瞬时向量与匹配模式

向量选择器用于从时间序列中筛选出特定标签组合的瞬时样本。根据返回结果的数量,分为 瞬时向量选择器 和 区间向量选择器。

类型 语法示例 返回结果 典型用途
精确匹配 http_requests_total{job="api-server", method="GET"} 一组时间序列(每个序列一个最新值) 单服务单方法请求量
正则匹配 http_requests_total{job=~"api-.*"} 匹配正则的所有序列 批量筛选同类服务
负向匹配 http_requests_total{job!~"api-.*"} 排除匹配正则的序列 排除特定分组
空标签匹配 http_requests_total{job=""} 标签不存在或值为空的序列 检测缺失标签
性能陷阱: 正则匹配 =~ 和 !~ 会触发全量标签扫描。在每秒数千次查询的高负载场景下,应优先使用精确匹配 = 或 !=。如果必须使用正则,确保正则表达式以固定前缀开头(如 api-.*),避免通配符开头。

2. 范围向量:时间窗口与数据密度

范围向量在瞬时向量的基础上附加时间窗口,返回窗口内所有样本点。语法为在指标名后加 [时间],例如 http_requests_total[5m]。

范围向量的核心参数是 步长(step) 和 窗口大小(range)。两者关系直接影响查询性能与结果精度:

  • 窗口 > 步长:数据重叠,产生平滑效果,但计算量增大。
  • 窗口 = 步长:无重叠,每个点独立计算,性能最优。
  • 窗口 < 步长:数据丢失,不推荐。
最佳实践: 对于 15s 采集间隔的指标,5m 窗口包含约 20 个样本点。在 rate() 或 avg_over_time() 等函数中,窗口大小应至少为采集间隔的 4 倍,以保证统计意义。例如:rate(http_requests_total[5m])。

3. 聚合操作符:分组与降维

聚合操作符将多个时间序列合并为更少的序列,是 Golden Signals 仪表盘中最常用的操作。常用聚合操作符包括:

操作符 作用 示例 性能影响
sum 求和 sum(rate(http_requests_total[5m])) by (job) 低,线性扩展
avg 平均值 avg(rate(http_requests_total[5m])) by (job) 低
max / min 最大值 / 最小值 max(rate(http_requests_total[5m])) by (job) 低
quantile 分位数(0-1) quantile(0.99, rate(http_requests_duration_seconds_bucket[5m])) 高,需预聚合
topk / bottomk Top K / Bottom K topk(5, rate(http_requests_total[5m])) 中,需排序
降维策略: 使用 by (label) 保留关键维度,丢弃无关标签。例如 sum by (job, instance) 比 sum by (job) 多保留一个维度,但序列数可能增加 10 倍。在仪表盘查询中,优先使用 by (job) 或 by (service),避免 by (instance) 导致大量序列。

4. 向量选择器、范围向量与聚合的协同流程

一个典型的 Golden Signals 查询通常按以下顺序组合:

  1. 选择目标序列:使用向量选择器过滤标签,缩小数据范围。
  2. 定义时间窗口:使用范围向量指定窗口大小,为速率或平均函数提供数据。
  3. 应用函数:如 rate()、increase()、avg_over_time()。
  4. 聚合降维:使用 sum by () 或 avg by () 合并序列。
  5. 二次运算:如 /、* 进行比率计算。

以下 SVG 图展示了这一完整流程:

PromQL 查询执行流程 向量选择器 标签过滤 范围向量 时间窗口 [5m] 速率/平均函数 rate() / avg_over_time() 聚合操作符 sum by (job) 关键原则: 1. 先过滤(向量选择器)→ 再窗口(范围向量)→ 再函数 → 最后聚合,减少中间数据量。 2. 避免在聚合后使用正则匹配,会丢失索引优化。 ✅ 高效查询 sum(rate(http_requests_total{job="api"}[5m])) by (job) ❌ 低效查询 sum(rate(http_requests_total[5m])) by (instance, job)

5. 常见错误与性能调优对照表

错误模式 错误示例 正确做法 性能提升
无标签过滤 rate(http_requests_total[5m]) rate(http_requests_total{job="api"}[5m]) 减少 80% 序列扫描
窗口过小 rate(http_requests_total[30s]) rate(http_requests_total[5m]) 避免数据抖动,减少计算次数
聚合维度过多 sum by (job, instance, method, status) sum by (job, method) 序列数减少 90%
嵌套聚合 sum(rate(sum(rate(...)))) 先聚合再 rate,或使用 recording rules 避免重复计算
分位数在原始数据上计算 quantile(0.99, http_request_duration_seconds) 使用 histogram_quantile 配合 bucket 降低内存消耗 10 倍

6. 聚合操作符的 by 与 without 选择

by 保留指定标签,丢弃其余标签;without 移除指定标签,保留其余标签。两者性能差异不大,但语义不同:

  • by (job):结果中只保留 job 标签,其他标签全部丢弃。适用于明确知道需要哪些维度。
  • without (instance):结果中移除 instance 标签,保留所有其他标签。适用于需要保留多个维度但排除特定标签。
推荐: 在 Golden Signals 仪表盘中,优先使用 by 显式声明保留维度。这比 without 更易读,且能避免意外保留过多标签导致序列爆炸。例如:sum by (job, service) (rate(...))。

7. 范围向量与聚合的联合使用示例

以下是一个完整的 Golden Signals 错误率查询,展示向量选择器、范围向量、函数和聚合的协同:

# 计算每个 job 的 5 分钟错误率(HTTP 5xx)
sum by (job) (
  rate(
    http_requests_total{status=~"5[0-9][0-9]"}[5m]
  )
)
/
sum by (job) (
  rate(
    http_requests_total[5m]
  )
)
* 100

该查询的执行顺序:

  1. 向量选择器 {status=~"5[0-9][0-9]"} 过滤出 5xx 状态码的序列。
  2. 范围向量 [5m] 为每个序列提供 5 分钟窗口数据。
  3. rate() 计算每秒增长率。
  4. sum by (job) 按 job 聚合所有实例的速率。
  5. 分子与分母相除,乘以 100 得到百分比。