PromQL 深度语法
PromQL(Prometheus Query Language)是 Prometheus 的核心能力,也是整个可观测性生态中最常用的指标查询语言。掌握 PromQL,是做指标分析、仪表盘配置和告警规则编写的基础。
但 PromQL 的学习曲线比较陡:语法简洁但含义丰富,容易写错但不易发现,查询结果可能和预期不一致。本文从实际使用场景出发,系统讲解 PromQL 的核心语法和常见模式。
基础概念
时间序列的表示
PromQL 的基本查询单位是时间序列(Time Series),每条时间序列由指标名称和标签(Label)唯一标识:
http_requests_total{service="order-api", method="GET", status="200"}
↑ ↑ ↑
指标名称 标签(多组) 标签值
标签决定了你如何「切片」指标。http_requests_total 是总量,加了 status="200" 就是成功请求数,加了 method="GET" 就是 GET 请求数。
四种 PromQL 表达式类型
即时向量用于告警和当前状态查询,区间向量用于计算变化率(rate)。
核心函数
rate() 和 irate()
rate() 是最常用的函数之一,用于计算每秒平均变化率。它假设在指定时间窗口内变化是线性的,适用于告警和仪表盘。
# 计算过去 5 分钟的平均请求速率(QPS)
rate(http_requests_total[5m])
# 按服务维度分解
sum(rate(http_requests_total[5m])) by (service)
irate() 用于计算瞬时变化率,适用于快速变化的数据(比如 CPU 使用率的尖峰)。但因为它是瞬时的,容易被噪声干扰,一般只用于绑图,不用于告警。
# 瞬时变化率,用于查看峰值
irate(http_requests_total{service="order-api"}[1m])
histogram_quantile()
计算分位数。这是指标分析中最核心的函数之一:
# P99 延迟(假设指标是 Histogram 类型)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
# 按服务分解的 P99
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)
histogram_quantile 的工作原理:Prometheus 先聚合各实例的 Bucket,再计算分位数。关键是要用 by (le) 聚合,因为 le(less than or equal)是 Histogram Bucket 的边界标签。
常见函数速查
聚合操作符
PromQL 的聚合操作符用于对时间序列进行分组和聚合:
# 按 service 标签聚合,计算 QPS
sum(rate(http_requests_total[5m])) by (service)
# 按 service 和 status 双重聚合
sum(rate(http_requests_total[5m])) by (service, status)
# 聚合后重命名标签
sum(rate(http_requests_total[5m])) by (service) keep_common
内置聚合函数
聚合操作符有对应的内置函数,行为更一致:
# 内置聚合函数版本(推荐)
sum by (service) (rate(http_requests_total[5m]))
min by (service) (http_requests_total)
max by (service) (http_requests_total)
avg by (service) (rate(http_requests_total[5m]))
by 子句指定按哪些标签分组,不在 by 中的标签会被丢弃。
过滤与匹配
标签匹配器
# 匹配多个服务
{service=~"order-api|user-api|payment-api"}
# 排除测试服务
{service!~"test-.*|dev-.*"}
布尔运算符
# 基础布尔运算
# 注意:布尔运算要求左右两边时间序列的标签完全匹配
# 错误率(需要 / 除法)
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# 使用 > 过滤(只看 CPU > 80% 的实例)
node_cpu_usage > 0.8
常见查询模式
模式一:QPS 计算
# 单服务 QPS
sum(rate(http_requests_total{service="order-api"}[5m]))
# 按 HTTP 方法分解 QPS
sum(rate(http_requests_total{service="order-api"}[5m])) by (method)
# 按状态码分解
sum(rate(http_requests_total{service="order-api"}[5m])) by (status)
模式二:延迟分位数
# P50 延迟
histogram_quantile(0.50,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
# P99 延迟(按服务分解)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)
# P99.9 延迟
histogram_quantile(0.999,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
模式三:错误率
# HTTP 5xx 错误率(百分比)
100
* sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/ sum(rate(http_requests_total[5m])) by (service)
# 5 分钟窗口内的错误数
increase(http_requests_total{status=~"5.."}[5m])
模式四:资源使用率
# CPU 使用率(所有核心平均)
100 * (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance))
# 内存使用率
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
# 磁盘使用率
100 * (1 - node_filesystem_avail_bytes / node_filesystem_size_bytes)
{mountpoint="/"}
模式五:对比增长率
# 当前 QPS 与 7 天前的 QPS 对比
sum(rate(http_requests_total[5m])) by (service)
/
sum(rate(http_requests_total[5m] offset 7d)) by (service)
模式六:滑动窗口聚合
# 过去 1 小时的平均 QPS(而非过去 5 分钟)
avg_over_time(sum(rate(http_requests_total[1m]))[1h:5m])
# 过去 10 分钟的最大 CPU
max_over_time(node_cpu_usage[10m])
常见错误与调试
错误一:标签不一致导致除法结果为空
# 错误:左边有 (service) 标签,右边没有,结果为空
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
rate(http_requests_total[5m])
# 正确:两边都按 service 聚合
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
错误二:histogram_quantile 中缺少 by (le)
# 错误:没有按 le 聚合
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])))
# 正确:按 le 聚合
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)
错误三:offset 导致时区混淆
# 当前时间的 QPS
sum(rate(http_requests_total[5m]))
# 7 天前的 QPS(注意:7d 是日历周,不是固定 168 小时)
sum(rate(http_requests_total[5m] offset 7d))
子查询与嵌套查询
PromQL 支持子查询,用于更复杂的分析:
# 计算过去 1 小时的 P99 延迟的变化趋势
# 外层查询:1 小时窗口,每 5 分钟一个点
# 内层查询:5 分钟窗口的 P99
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)[1h:5m]
子查询的语法是 [outer_range:resolution]。上面的例子:外层是 1 小时范围,每 5 分钟一个数据点。
质量判断标准
读完本节后,你应该能够回答:
rate() 和 irate() 的核心区别是什么?分别适用于什么场景?
histogram_quantile 函数中 by (le) 为什么是必须的?省略会怎样?
- 当 PromQL 除法结果为空时,最可能的原因是什么?如何调试?
- 如何用 PromQL 计算一个服务的错误率(HTTP 5xx 的百分比)?
- offset 查询的作用是什么?在 SLO 告警中 offset 有什么特殊用途?