4、PromQL 入门:即时向量与范围向量、选择器、聚合操作符、常用函数(rate, irate, increase)
好,咱们进入 PromQL 的核心地带。说实话,很多刚接触 Prometheus 的朋友,第一反应是:「这查询语言怎么看着怪怪的?」
嗯,我当年也一样。但用顺手之后你会发现,PromQL 的设计其实非常优雅。它专门为时序数据而生,没有多余的废话。
今天这一讲,我带你把 PromQL 的骨架搭起来。掌握了这些,你就能看懂 80% 的告警规则和监控面板了。
4.1 即时向量与范围向量:理解 PromQL 的数据模型
这是 PromQL 最基础、也最容易混淆的概念。我刚开始学的时候,也在这上面栽过跟头。
4.1.1 即时向量(Instant Vector)
说白了,就是「当前这一刻」的数据快照。
比如你查 node_cpu_seconds_total,返回的是每个 CPU 核在当前时间点的最新值。每个时间序列只有一条数据。
关键特征:
- 每个时间序列只返回一个样本点
- 查询时不需要指定时间范围
- 常用于仪表盘上的「当前值」展示
举个例子:
node_memory_MemAvailable_bytes
这条查询返回的就是「此时此刻」每台机器的可用内存。嗯,就这么简单。
4.1.2 范围向量(Range Vector)
范围向量就不一样了。它返回的是「过去一段时间」的数据。
你想想看,如果你要看 CPU 使用率的变化趋势,光看当前值肯定不够。你需要过去 5 分钟的数据,对吧?
node_cpu_seconds_total[5m]
这条查询返回的是每个 CPU 核在过去 5 分钟内的所有采样点。注意,这里返回的是多个值,不是一个。
我曾经踩过的坑: 范围向量不能直接用于绘图!你必须配合函数(比如 rate())才能得到可用的结果。我第一次用 Grafana 时,直接把 [5m] 丢进去,结果面板一片空白。嗯,后来才明白,范围向量只是「原材料」,需要加工才能用。
| 类型 | 返回数据量 | 典型用途 | 示例 |
|---|---|---|---|
| 即时向量 | 每个序列 1 个值 | 当前状态、告警阈值 | node_load1 |
| 范围向量 | 每个序列 N 个值 | 趋势分析、速率计算 | node_load1[5m] |
4.2 选择器:怎么精确找到你要的数据
PromQL 的选择器,说白了就是「过滤条件」。你总不想查 CPU 时把内存数据也带出来吧?
4.2.1 精确匹配(=)
最常用的方式。我习惯用它来定位特定实例:
node_cpu_seconds_total{instance="192.168.1.1:9100"}
4.2.2 正则匹配(=~)
当你有一批机器需要一起查时,正则就派上用场了:
node_cpu_seconds_total{instance=~"192.168.1.*:9100"}
这个查询会匹配所有 192.168.1.x 段的机器。我在管理上百台服务器时,这个功能帮我省了不少事。
4.2.3 反向匹配(!=, !~)
排除某些数据。比如你想看所有非空闲的 CPU 状态:
node_cpu_seconds_total{mode!="idle"}
我的个人习惯: 写选择器时,尽量把过滤条件写精确。不要偷懒写 {job=~".*"},这会拉回所有数据,性能会很差。我曾经见过一个同事,一条查询跑了 30 秒还没出结果,就是因为选择器太宽泛了。
4.3 聚合操作符:把多条数据揉在一起
有时候你不需要看每台机器的细节,你只需要一个「总和」或「平均值」。聚合操作符就是干这个的。
4.3.1 常用聚合操作符
| 操作符 | 作用 | 我常用的场景 |
|---|---|---|
sum |
求和 | 计算所有机器的总 CPU 使用量 |
avg |
平均值 | 查看集群的平均负载 |
max / min |
最大 / 最小值 | 找出最忙和最闲的机器 |
count |
计数 | 统计有多少台机器在运行 |
topk / bottomk |
前 K 个 / 后 K 个 | 找出 CPU 最高的前 5 台机器 |
4.3.2 配合 by 子句使用
聚合时,你可以指定按什么维度分组。比如:
sum by (instance) (node_cpu_seconds_total{mode="idle"})
这条查询会按 instance 分组,计算每台机器的总空闲 CPU 时间。如果不加 by,它会把所有机器的数据揉成一个值——这通常不是我们想要的。
避坑指南: 我曾经在写告警规则时,忘了加 by 子句,结果聚合出来的值完全不对。一条本该触发告警的规则,硬是没响。嗯,从那以后,我每次写聚合都会先问自己:「我要按什么维度分组?」
4.4 常用函数:rate, irate, increase
这三个函数,是 PromQL 里最常用的「三剑客」。我敢说,你写的每三条查询里,至少有一条会用到它们。
4.4.1 rate():平滑的速率计算
rate() 计算的是「每秒平均增长率」。它会在指定时间范围内取平均值,所以曲线比较平滑。
rate(node_cpu_seconds_total{mode="idle"}[5m])
这条查询返回的是:过去 5 分钟内,CPU 空闲时间的每秒平均增长率。
为什么用 rate() 而不是直接看原始值?因为 Counter 类型的指标(比如 node_cpu_seconds_total)是单调递增的,直接看数值没有意义。你需要知道的是「增长的速度」。
我的建议: 在大多数场景下,优先用 rate()。它抗抖动能力强,适合做告警和长期趋势分析。
4.4.2 irate():灵敏的瞬时速率
irate() 计算的是「基于最后两个采样点的瞬时增长率」。它不取平均,所以对突发变化非常敏感。
irate(node_cpu_seconds_total{mode="idle"}[5m])
你看,语法和 rate() 几乎一样,但行为完全不同。
irate() 的曲线锯齿感很强,适合用来观察短时间内的突发流量。但我不建议用它来做告警——太灵敏了,容易误报。
| 函数 | 计算方式 | 曲线特点 | 推荐场景 |
|---|---|---|---|
rate() |
时间范围内平均 | 平滑 | 告警、长期趋势 |
irate() |
最后两个点 | 锯齿状、灵敏 | 实时监控、突发检测 |
4.4.3 increase():绝对增量
increase() 返回的是「指定时间范围内的总增量」。它和 rate() 本质上是同一个东西,只是单位不同。
increase(node_network_receive_bytes_total[1h])
这条查询告诉你:过去 1 小时内,每块网卡总共收到了多少字节。
我个人习惯用 increase() 来做容量规划。比如看过去 24 小时的总流量,用 increase() 就很直观。
注意: increase() 的结果受时间范围影响很大。同样是查网络流量,[5m] 和 [1h] 的结果会差很多倍。嗯,这听起来是废话,但我真见过有人把这两个搞混,然后告警阈值设得乱七八糟。
4.5 实战组合:一个完整的查询示例
好了,理论讲完了。咱们来写一条真实的查询,把今天学的串起来。
假设你想知道:当前集群中,CPU 使用率最高的前 5 台机器是哪些?
topk(5,
sum by (instance) (
rate(node_cpu_seconds_total{mode!="idle"}[5m])
)
)
这条查询干了三件事:
rate(...[5m]):计算每台机器每个 CPU 核的非空闲时间增长率sum by (instance):按机器聚合,得到每台机器的总 CPU 使用率topk(5, ...):取前 5 名
你看,把今天学的知识点组合起来,就能写出很实用的查询了。
最后说一句: PromQL 的学习,没有捷径。我建议你打开 Prometheus 的 Web UI,在「Graph」页面里多试。写错了没关系,PromQL 的报错信息还算友好。多练几次,你就能找到感觉了。