PromQL 语法、示例与优化
PromQL 操作的是带标签的时间序列。每写一条复杂表达式,都先在 Prometheus Graph 或 Grafana Explore 执行最简单的裸指标,确认指标名、标签和单位,再逐层增加选择器、函数和聚合。
1. 数据类型与选择器
| 类型 | 示例 | 说明 |
|---|---|---|
| Instant vector | up | 当前时刻每条序列的一个样本 |
| Range vector | up[5m] | 每条序列在 5 分钟窗口内的样本范围 |
| Scalar | 0.95 | 单个数值,可与向量运算 |
| String | 较少直接使用 | 函数参数中的字符串值 |
# 查询所有 up 时间序列,结果带 job、instance 等原始标签
up
# 精确匹配:只看生产 node-exporter
up{job="node-exporter", environment="production"}
# 正则匹配:同时选择 checkout 和 payment 服务
http_requests_total{service=~"checkout|payment"}
# 负向匹配:排除成功和重定向状态码
http_requests_total{code!~"2..|3.."}
# Range vector:过去 5 分钟样本,作为 rate() 的输入
http_requests_total[5m]
=/!= 是精确匹配,=~/!~ 是正则匹配。高基数标签上使用宽泛正则会增加查询成本;优先使用 cluster、environment、namespace、service 等稳定过滤条件。
2. 运算符与聚合
PromQL 支持算术、比较和集合运算。比较默认只保留满足条件的样本;加上 bool 后保留所有匹配样本,并将结果改为 0 或 1。
# 算术:计算内存使用率,结果单位为百分比
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
# 比较:只返回使用率高于 85 的序列
(100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 85
# bool 比较:所有序列都会返回,超过阈值为 1,否则为 0
(100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > bool 85
# 聚合:按 service 汇总每秒请求数,丢弃其余标签
sum by (service) (rate(http_requests_total[5m]))
# 聚合:保留除 instance、pod 外的全部标签后求和
sum without (instance, pod) (rate(http_requests_total[5m]))
常用聚合包括 sum、avg、min、max、count、stddev、topk 和 bottomk。聚合前先确认是否应保留 cluster、namespace、service 等业务边界,避免把多个环境混在一起。
3. Counter、Gauge 与常用函数
| 函数 | 输入 | 适用场景 | 注意事项 |
|---|---|---|---|
rate() | Counter Range vector | 稳定的每秒速率 | 窗口至少覆盖 4 个抓取周期 |
irate() | Counter Range vector | 瞬时变化图表 | 对抖动敏感,不用于常规告警 |
increase() | Counter Range vector | 窗口内累计次数 | 适合批任务、失败次数 |
avg_over_time() | Gauge Range vector | 窗口平均值 | 不要用于 Counter 累计值 |
histogram_quantile() | Histogram bucket 速率 | P95/P99 延迟 | 聚合必须保留 le 标签 |
predict_linear() | Gauge Range vector | 容量趋势预测 | 仅适合近似稳定的趋势 |
# Counter:过去 5 分钟平均每秒请求数
rate(http_requests_total[5m])
# Counter:过去 1 小时累计失败次数,按 job 汇总
sum by (job) (increase(batch_job_failures_total[1h]))
# Gauge:过去 30 分钟平均可用内存,按实例展示
avg_over_time(node_memory_MemAvailable_bytes[30m])
4. 向量匹配
二元运算要求两侧标签可匹配。默认使用两侧相同标签匹配;on() 指定匹配键,ignoring() 忽略某些标签。group_left/group_right 只在确认一对多关系时使用。
# 错误率:分子和分母都按 cluster、service 聚合,确保标签维度一致
sum by (cluster, service) (rate(http_requests_total{code=~"5.."}[5m]))
/
sum by (cluster, service) (rate(http_requests_total[5m]))
# 将服务所属团队信息关联到服务错误数;team_info 每个 service 只能有一条
sum by (service) (rate(http_requests_total{code=~"5.."}[5m]))
* on (service) group_left (team) service_team_info
若出现 “many-to-many matching not allowed”,分别运行算式两边,检查各自的标签和序列数量,再决定是否先聚合或修复元数据唯一性。
5. 常见查询案例
CPU 使用率
# 计算每台主机最近 5 分钟平均 CPU 使用率(百分比)
# 先按 instance 对 idle CPU 秒数取平均,再用 100 减去空闲比例
100 - avg by (instance) (
rate(node_cpu_seconds_total{mode="idle"}[5m])
) * 100
内存、磁盘和网络
# 内存使用率:MemAvailable 已考虑可回收缓存,通常比 Free 更有意义
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
# 根分区使用率:排除 tmpfs、overlay 等不代表宿主机磁盘容量的文件系统
100 * (1 - node_filesystem_avail_bytes{mountpoint="/",fstype!~"tmpfs|overlay"}
/ node_filesystem_size_bytes{mountpoint="/",fstype!~"tmpfs|overlay"})
# 每实例网络接收速率,单位为 bytes/s;排除 loopback 和临时 veth 接口
sum by (instance) (
rate(node_network_receive_bytes_total{device!~"lo|veth.*"}[5m])
)
QPS、错误率与延迟
# QPS:按服务汇总最近 5 分钟每秒请求数
sum by (service) (rate(http_requests_total[5m]))
# 5xx 错误率:结果为 0 到 1,Grafana 可格式化为百分比
sum by (service) (rate(http_requests_total{code=~"5.."}[5m]))
/
sum by (service) (rate(http_requests_total[5m]))
# P95 延迟:Histogram bucket 聚合时必须包含 le 和 service 标签
histogram_quantile(0.95,
sum by (le, service) (
rate(http_request_duration_seconds_bucket[5m])
)
)
Histogram 分位数是近似值,精度由 bucket 边界决定。若延迟 SLO 为 300 ms,bucket 设计必须覆盖该阈值附近,而不是只保留 1 秒和 5 秒等粗粒度桶。
6. 查询性能优化
- 先限制时间范围、
cluster/namespace/service标签,再执行聚合。 - 对重复且昂贵的表达式创建 Recording Rule,供 Dashboard 与告警共享。
- Grafana 使用
$__rate_interval,避免用户缩放时间范围后速率窗口不合理。 - 避免在告警中使用
irate()、无界正则和深层子查询。 - 使用 Prometheus 查询页面、Grafana Query Inspector 与
prometheus_engine_query_duration_seconds找出慢查询。
指标不存在、结果为空或 TSDB 异常时,参见 Prometheus 故障排查。