4. PromQL 查询语言:即时查询、范围查询、聚合操作、Rate/Irate 函数
PromQL 是 Prometheus 的查询语言。说白了,它就是你和监控数据对话的方式。我见过不少运维同事,部署了 Prometheus 却只会看几个预制面板,遇到问题就抓瞎。其实 PromQL 没那么玄乎,掌握几个核心概念,你就能把监控数据玩出花来。
4.1 即时查询与范围查询
这是 PromQL 最基础的两个概念。我刚开始学的时候也混淆过,后来用一句话就记住了:即时查询看当前,范围查询看趋势。
4.1.1 即时查询(Instant Query)
即时查询返回的是某一时刻的样本值。你想想看,就像给监控数据拍了一张快照。
# 查询当前时刻的 CPU 使用率
node_cpu_seconds_total{mode="idle"}
# 查询当前时刻的内存使用量
node_memory_MemAvailable_bytes
返回结果是一个向量,每个时间序列只有一个值。我在项目中遇到过一个问题:用即时查询做告警,结果因为数据采集间隔和告警评估周期没对齐,导致告警频繁抖动。后来我加了个小技巧——用 offset 偏移一下时间,问题就解决了。
offset 可以查看历史某个时刻的数据。比如 node_cpu_seconds_total offset 5m 就是看 5 分钟前的数据。
4.1.2 范围查询(Range Query)
范围查询返回的是一段时间内的样本值。它返回的是一个矩阵,每个时间序列包含多个值。
# 查询过去 5 分钟的 CPU 使用率
node_cpu_seconds_total{mode="idle"}[5m]
# 查询过去 1 小时的内存使用量
node_memory_MemAvailable_bytes[1h]
范围查询的语法就是在指标名后面加 [时间范围]。时间范围支持 s(秒)、m(分)、h(时)、d(天)。
| 查询类型 | 返回类型 | 典型场景 |
|---|---|---|
| 即时查询 | 瞬时向量 | 告警判断、当前状态查看 |
| 范围查询 | 区间向量 | 趋势分析、聚合计算、绘图 |
4.2 聚合操作
聚合操作就是把多个时间序列合并成一个。为什么要做聚合?因为监控数据太细了。比如你有 100 台机器的 CPU 使用率,你不可能每台都看一遍,你需要一个整体视图。
4.2.1 常用聚合函数
PromQL 提供了丰富的聚合函数,我挑几个最常用的说说:
sum:求和。比如所有机器的 CPU 总使用量。avg:平均值。比如集群的平均 CPU 使用率。max/min:最大值 / 最小值。比如找出 CPU 使用率最高的机器。count:计数。比如有多少台机器 CPU 使用率超过 80%。quantile:分位数。比如 P99 响应时间。
# 所有机器的平均 CPU 使用率
avg(node_cpu_seconds_total{mode="idle"})
# 每个机房的 CPU 总使用量
sum(node_cpu_seconds_total{mode="idle"}) by (机房)
# 找出 CPU 使用率最高的机器
max(node_cpu_seconds_total{mode="idle"}) by (instance)
4.2.2 分组聚合
分组聚合是聚合操作的精髓。它允许你按照某个标签维度进行聚合。
# 按 instance 分组,计算每个实例的 CPU 使用率
avg(node_cpu_seconds_total{mode="idle"}) by (instance)
# 按 job 和 instance 分组
avg(node_cpu_seconds_total{mode="idle"}) by (job, instance)
我个人习惯在分组聚合时,只保留必要的标签。标签越多,返回的时间序列就越多,查询效率就越低。我曾经接手过一个面板,里面有个查询用了 by (instance, job, 机房, 集群, 环境),结果每次加载都要等十几秒。后来我砍掉了不必要的标签,查询时间降到了 1 秒以内。
4.3 Rate / Irate 函数
这两个函数是处理计数器类型指标的利器。计数器(Counter)的特点是只增不减,比如请求总数、CPU 总时间。直接看计数器本身没有意义,你需要看它的变化率。
4.3.1 Rate 函数
rate() 计算的是一段时间内的平均每秒增长率。它会在指定时间范围内计算所有样本的变化率,然后取平均值。
# 过去 5 分钟的平均每秒请求数
rate(http_requests_total[5m])
# 过去 1 小时的平均每秒 CPU 使用率
rate(node_cpu_seconds_total{mode="idle"}[1h])
rate() 适合用于平滑的趋势分析。因为它取了平均值,所以曲线会比较平滑,不会因为个别数据点抖动而剧烈变化。
4.3.2 Irate 函数
irate() 计算的是区间内最后两个样本的瞬时增长率。它只取时间范围内的最后两个数据点,计算它们之间的变化率。
# 过去 5 分钟的瞬时请求数
irate(http_requests_total[5m])
# 过去 1 小时的瞬时 CPU 使用率
irate(node_cpu_seconds_total{mode="idle"}[1h])
irate() 适合用于检测突发的尖峰。因为它只关注最后两个点,所以对变化非常敏感。但缺点也很明显——曲线会非常抖,容易产生误告警。
| 函数 | 计算方式 | 曲线特点 | 适用场景 |
|---|---|---|---|
| rate() | 区间内所有样本的平均增长率 | 平滑 | 趋势分析、长期告警 |
| irate() | 区间内最后两个样本的增长率 | 抖动 | 突发检测、短期告警 |
rate(),因为它更稳定。面板展示可以用 irate(),因为它能更快反映变化。我曾经在告警规则里用了 irate(),结果因为网络抖动导致频繁误告警,后来改成 rate() 就稳了。
4.4 知识体系总览
下面这张图总结了 PromQL 的核心知识结构,你可以把它当作一个快速参考:
4.5 实战:构建一个完整的查询
光说不练假把式。我们来看一个完整的例子:监控集群的 CPU 使用率。
# 1. 原始数据:每个 CPU 核的空闲时间
node_cpu_seconds_total{mode="idle"}
# 2. 计算每个核的 CPU 使用率(1 - 空闲率)
1 - rate(node_cpu_seconds_total{mode="idle"}[5m])
# 3. 按 instance 聚合,计算每台机器的平均 CPU 使用率
avg(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)
# 4. 只显示 CPU 使用率超过 80% 的机器
avg(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) > 0.8
这个查询从原始数据开始,一步步加工,最终得到了我们想要的结果。我在项目中经常用这种逐步构建的方法,先在 Prometheus 的查询面板里试,确认每一步的结果都对,再放到告警规则里。