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 查询通常按以下顺序组合:
- 选择目标序列:使用向量选择器过滤标签,缩小数据范围。
- 定义时间窗口:使用范围向量指定窗口大小,为速率或平均函数提供数据。
- 应用函数:如
rate()、increase()、avg_over_time()。 - 聚合降维:使用
sum by ()或avg by ()合并序列。 - 二次运算:如
/、*进行比率计算。
以下 SVG 图展示了这一完整流程:
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
该查询的执行顺序:
- 向量选择器
{status=~"5[0-9][0-9]"}过滤出 5xx 状态码的序列。 - 范围向量
[5m]为每个序列提供 5 分钟窗口数据。 rate()计算每秒增长率。sum by (job)按 job 聚合所有实例的速率。- 分子与分母相除,乘以 100 得到百分比。