4. PromQL 入门:数据类型、选择器、操作符、常用函数与聚合
好,咱们进入 PromQL 的世界。说实话,PromQL 是 Prometheus 监控的灵魂。你指标采集得再好,告警规则写得再全,不会查 PromQL,等于白搭。我见过太多运维同学,对着 Prometheus 的 UI 界面发呆,想查个东西不知道从哪下手。今天咱们就把这块彻底讲透。
4.1 数据类型:就四种,别怕
PromQL 的数据类型,说白了就四种。你记住它们,后面就好办了。
| 类型 | 说明 | 我常用的场景 |
|---|---|---|
| 瞬时向量 | 某个时间点的一组时间序列 | 查当前 CPU 使用率、内存占用 |
| 区间向量 | 一段时间内的一组时间序列 | 看过去 5 分钟的流量趋势 |
| 标量 | 一个单纯的数字 | 比如阈值 0.8、告警级别 3 |
| 字符串 | 文本值,用得少 | 偶尔在告警描述里用一下 |
嗯,这里要注意:瞬时向量是你最常用的。比如 node_cpu_seconds_total 查出来就是一堆带标签的时间序列,每个序列在当前时间点有一个值。而区间向量呢,就是给这个查询加个时间范围,比如 node_cpu_seconds_total[5m],返回的是过去 5 分钟每个时间点的数据。
4.2 选择器:怎么精准找到你要的数据
选择器就是你的「数据过滤器」。我刚开始用的时候,经常因为标签写错一个字母,查出来空数据,排查半天。
4.2.1 精确匹配
最基础的,用 = 和 !=:
# 查某个实例的 CPU 使用率
node_cpu_seconds_total{instance="192.168.1.1:9100", mode="idle"}
# 排除某个实例
node_cpu_seconds_total{instance!="192.168.1.1:9100"}
4.2.2 正则匹配
这个我项目中用得特别多。比如你要查所有 web 服务的指标:
# 匹配以 "web-" 开头的服务
http_requests_total{service=~"web-.*"}
# 排除非生产环境
http_requests_total{env!~"dev|test"}
= 就别用 =~。
4.3 操作符:加减乘除与比较
操作符这块,说白了就是数学运算。但 PromQL 的操作符有个特点——它是向量之间的运算,不是简单的数字运算。
4.3.1 算术操作符
加减乘除、取模、幂运算,这些跟普通数学一样:
# 计算 CPU 使用率(非空闲时间占比)
1 - (node_cpu_seconds_total{mode="idle"} / node_cpu_seconds_total)
# 计算内存使用率
node_memory_MemTotal_bytes - node_memory_MemFree_bytes
4.3.2 比较操作符
这个在告警规则里是核心。你想想看,告警不就是「当某个值大于阈值时触发」吗?
# 找出 CPU 使用率超过 80% 的实例
(1 - (node_cpu_seconds_total{mode="idle"} / node_cpu_seconds_total)) > 0.8
4.3.3 逻辑操作符
and、or、unless 这三个,我用的最多的是 and:
# 同时满足 CPU 高且内存高的实例
(CPU使用率 > 0.8) and (内存使用率 > 0.8)
on() 或 ignoring() 来指定匹配规则。这个我后面会专门讲。
4.4 常用函数:这些你必须会
PromQL 的函数很多,但常用的就那几个。我按使用频率给你排个序:
4.4.1 rate() 和 irate()
这两个是处理 Counter 类型指标的利器。Counter 是只增不减的,比如请求总数、错误总数。你要看「每秒变化率」,就得用它们。
# 每秒请求数(推荐用 rate)
rate(http_requests_total[5m])
# 瞬时请求率(对突发敏感时用)
irate(http_requests_total[5m])
我个人习惯用 rate(),因为它平滑了数据,告警不容易抖动。但如果你需要快速响应突发流量,irate() 更合适。
4.4.2 increase()
这个函数返回的是「一段时间内的增量」。比如你想知道过去 1 小时有多少请求:
increase(http_requests_total[1h])
4.4.3 histogram_quantile()
这个函数用来计算百分位数,比如 P99 响应时间。我在做性能分析时必用:
# 计算过去 5 分钟的 P99 响应时间
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
4.5 聚合操作:从全局看问题
单个实例的数据只能看到局部,聚合才能看到全局。PromQL 的聚合操作符,说白了就是「分组统计」。
4.5.1 常用聚合操作符
| 操作符 | 作用 | 我常用的场景 |
|---|---|---|
sum() |
求和 | 所有实例的总请求数 |
avg() |
平均值 | 集群的平均 CPU 使用率 |
max() |
最大值 | 找出最忙的实例 |
min() |
最小值 | 找出最空闲的实例 |
count() |
计数 | 有多少个实例在运行 |
topk() |
取前 K 个 | 找出请求量最高的 5 个服务 |
4.5.2 配合 by() 分组
这才是聚合的精髓。不加 by(),所有数据揉在一起。加了 by(),按标签分组统计:
# 按 service 分组,统计每个服务的总请求数
sum(rate(http_requests_total[5m])) by (service)
# 按 instance 分组,统计每个实例的 CPU 使用率
avg(node_cpu_seconds_total{mode="idle"}) by (instance)
4.5.3 without() 排除标签
without() 是 by() 的反向操作。它说「除了这些标签,其他都保留」:
# 排除 instance 标签,按其他所有标签聚合
sum(rate(http_requests_total[5m])) without (instance)
by() 和 without()。后来我记住一个原则:by() 是「我要按什么分组」,without() 是「我不要什么标签」。你想想看,哪个更符合你的思维习惯就用哪个。
4.6 实战:一个完整的查询示例
咱们来一个综合的例子。假设你要监控一个微服务集群,想知道「过去 5 分钟,每个服务的 P99 响应时间,并且只关注那些请求量超过 100 QPS 的服务」:
# 第一步:计算每个服务的 P99 响应时间
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (service, le)
)
# 第二步:计算每个服务的 QPS
sum(rate(http_requests_total[5m])) by (service)
# 第三步:组合起来,只显示 QPS > 100 的服务
(
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (service, le)
)
)
and
(
sum(rate(http_requests_total[5m])) by (service) > 100
)
这个查询看起来复杂,但拆开看就清楚了。先算 P99,再算 QPS,最后用 and 取交集。我在实际项目中,经常用这种「分步组合」的方式来写复杂查询。