PromQL介绍原创
Prometheus Query Language (PromQL) 是一种功能强大的查询语言,用于在 Prometheus 中查询和分析时间序列数据。以下是一些常用的 PromQL 查询示例,帮助你在实际监控和分析中使用。
版本说明
本文写于 2023-06,2026-08 复核。
文中所有函数(rate / increase / avg_over_time / topk / delta)与聚合语法自 Prometheus 2.x 起未变,在 3.x 上同样适用。
指标名以 node_exporter 1.x 为准;跨大版本升级 exporter 时请用 curl localhost:9100/metrics | grep <指标名> 确认指标仍然存在。
# 基本查询
简单查询: 查询某个指标的当前值。
node_cpu_seconds_total1带条件的查询: 查询某个指标在特定条件下的当前值。
node_cpu_seconds_total{mode="idle"}1
# 聚合操作
聚合函数默认会把所有匹配到的时间序列压成一条,这几乎不是你想要的——一个 200 台机器的集群,avg(node_memory_MemAvailable_bytes) 得到的是「全集群平均可用内存」,某台机器快 OOM 了这个数字也看不出来。实际使用时几乎总要带 by (instance),把聚合限制在单机维度内:
按实例求平均值:
avg by (instance) (node_memory_MemAvailable_bytes)1按实例求和(多网卡、多磁盘这类单机内有多条序列的指标才有意义):
sum by (instance) (node_network_receive_bytes_total)1最大值 / 最小值:
max by (instance) (node_memory_MemAvailable_bytes) min by (instance) (node_memory_MemAvailable_bytes)1
2不带
by的max(...)/min(...)只在你确实想问「全集群最极端的那个值是多少」时才用,且这种写法丢失了 instance 标签——你会知道最小值是 300MB,但不知道是哪台机器。要同时拿到值和机器名,用bottomk(1, node_memory_MemAvailable_bytes)。
# 计算速率
- 计算速率:
计算某个指标在一段时间内的变化速率。
rate(node_cpu_seconds_total{mode="idle"}[5m])1
# 分组和子查询
按标签分组: 计算某个指标在不同标签维度上的总和。
sum(rate(node_cpu_seconds_total[5m])) by (instance)1分组并求平均值: 按某个标签分组并计算平均值。
avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance)1别把 `avg` 直接套在多 mode 的 CPU 指标上
上面这条特意加了
mode="idle"。node_cpu_seconds_total同时带cpu和mode两个维度,写成avg(rate(node_cpu_seconds_total[5m])) by (instance)是把 8 个 mode 的速率一起求平均,得到的数值没有物理含义,不能当 CPU 使用率用(会比真实值小若干倍)。限定单个 mode 后,avg跨的才是 CPU 核,语义才成立。完整推导见 Prometheus 监控介绍。
# 比率和百分比
计算CPU使用率: 计算 CPU 使用率(非空闲时间占比)。
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)1内存使用百分比: 计算内存使用百分比。
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 1001
# 结合时间范围
查询过去5分钟的平均值: 计算过去5分钟某个指标的平均值。
avg_over_time(node_load1[5m])1查询特定时间点的值: 查询某个指标在特定时间点的值。
node_load1 @ 16224768001
# 常用的Prometheus报警规则
CPU使用率报警: 当某个实例的CPU使用率超过80%时触发报警。
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 801内存使用率报警: 当某个实例的内存使用率超过90%时触发报警。
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 901磁盘使用率报警: 当某个实例的根目录磁盘使用率超过90%时触发报警。
( node_filesystem_size_bytes{mountpoint="/", fstype!~"tmpfs|overlay|squashfs"} - node_filesystem_avail_bytes{mountpoint="/", fstype!~"tmpfs|overlay|squashfs"} ) / node_filesystem_size_bytes{mountpoint="/", fstype!~"tmpfs|overlay|squashfs"} * 100 > 901
2
3
4
5必须用
avail而不是free:node_filesystem_free_bytes是文件系统层面的空闲块总数,其中包含了 ext4 默认预留给 root 的 5%(mke2fs的-m 5);node_filesystem_avail_bytes才是非特权进程实际能用的空间。用free计算会系统性低估使用率约 5 个百分点——普通业务进程已经写不进去并开始报No space left on device了,用free算出来的使用率可能还停在 95% 以下,告警根本不触发。这是磁盘告警最经典的漏报原因。fstype!~那一段是排除 tmpfs、容器 overlay 层这类伪文件系统,否则一台跑 Docker 的机器会刷出几十条无意义的告警。
# 高级查询
查询前N个值: 查询某个指标的前5个最大值。
topk(5, node_load1)1查询区间增量: 计算某个 counter 指标在过去一小时内增长了多少。
increase(node_network_receive_bytes_total[1h])1increase和delta不能混用:node_network_receive_bytes_total名字里的_total后缀表明它是 counter(只增不减,进程重启时归零)。delta()按官方定义只适用于 gauge,它做的是「区间末值 - 区间首值」的外推,遇到计数器重置会算出负数——网卡计数器溢出或 node_exporter 重启,这个表达式就会给出一个荒谬的负增量。increase()(以及rate())内部有 counter reset 检测,会自动把重置那一段补偿回来。判断法则很简单:指标名以
_total/_count/_sum结尾的用rate/increase,其余(内存、温度、队列长度这类可增可减的)才用delta/deriv。
# 坑与边界
1. 区间向量的时长至少要覆盖 4 个采样点。 rate(x[5m]) 在 scrape_interval: 15s 下有 20 个点,很充裕;但如果某个 job 的抓取间隔是 2 分钟,[5m] 只有 2–3 个点,Prometheus 可能直接返回空值。经验法则是区间时长 ≥ 4 × scrape_interval,告警规则里尤其要注意——一条永远返回空的告警规则不会报错,它只是永远不触发。
2. rate 与 irate 不能凭感觉选。 rate 是整个区间的平均速率,曲线平滑,适合告警和趋势图;irate 只取区间内最后两个点,对尖刺极其敏感,适合排障时看瞬时抖动。告警规则里不要用 irate——它会因为单次采样抖动而反复触发和恢复。
3. 聚合会丢标签,且丢得静默。 sum(rate(http_requests_total[5m])) 把所有 method、status、instance 揉成一个数,不会有任何警告。写完聚合表达式后,先在 Graph 页面切到 Table 视图看一眼返回了几条序列、带哪些标签,再决定要不要加 by / without。
4. 告警表达式里的 > 90 是瞬时判断,必须配 for。 PromQL 表达式本身只回答「此刻是否超阈值」。真正的告警规则要在 YAML 里写 for: 5m,表示持续 5 分钟超阈值才发。缺了 for,一次采样抖动就会发一封告警邮件。
5. @ 修饰符(第 13 条)需要 Prometheus 2.25+,且默认关闭。 早期版本需要启动时加 --enable-feature=promql-at-modifier 才能用,2.29 之后才成为默认可用。在老集群上直接写 @ 会报解析错误。