PromQL详解:深入理解Prometheus查询语言
PromQL (Prometheus Query Language) 是 Prometheus 监控系统中用于实时查询和聚合时间序列数据的强大表达式语言。它是 Prometheus 核心价值的体现之一,通过 PromQL,用户可以灵活地对指标数据进行筛选、聚合、计算和转换,从而深入洞察系统行为、发现问题模式并支持警报规则的定义。掌握 PromQL 是有效利用 Prometheus 进行监控和可观测性的关键。
核心思想:
PromQL 允许用户通过组合指标名称、标签选择器、范围选择器、运算符和函数,从 Prometheus 的时序数据库 (TSDB) 中提取、转换和计算数据。它的多维数据模型和向量操作使其在处理和分析复杂的时序数据方面表现出色。
一、PromQL 基础
PromQL 表达式返回的结果类型可以是以下四种之一:
- 即时向量 (Instant vector):由一组时间序列组成,每个时间序列包含一个时间戳和一个样本值。所有时间序列共享相同的时间戳。例如:
http_requests_total。 - 范围向量 (Range vector):由一组时间序列组成,每个时间序列包含在给定时间范围内的多个样本值。例如:
http_requests_total[5m]。 - 纯量 (Scalar):一个简单的浮点数。例如:
100。 - 字符串 (String):一个简单的字符串(在 PromQL 中不常用)。例如:
"up"。
1.1 指标选择器 (Metric Selectors)
PromQL 查询的核心是选择要操作的时间序列。
1.1.1 指标名称 (Metric Name)
最简单的查询是只指定指标名称。这将返回该指标名称下所有标签组合的最新即时向量。
1 | # 获取所有 http_requests_total 指标的最新值 |
1.1.2 标签匹配 (Label Matching)
通过花括号 {} 可以添加标签匹配器来过滤时间序列。
=(精确匹配):选择标签值完全等于给定字符串的。!=(不等于):选择标签值不等于给定字符串的。=~(正则匹配):选择标签值匹配给定正则表达式的。!~(正则不匹配):选择标签值不匹配给定正则表达式的。
1 | # 选择 job 为 prometheus 且 instance 为 localhost:9090 的时间序列 |
特殊标签:__name__
指标名称本身在内部被视为一个名为 __name__ 的标签。这意味着你可以使用标签匹配器来选择或过滤指标名称。
1 | # 等同于 http_requests_total |
1.1.3 范围选择器 (Range Vector Selectors)
范围选择器通过在即时向量选择器后加上方括号 [] 和一个持续时间来获取一段时间内的样本数据,返回一个范围向量。持续时间可以用 s (秒), m (分钟), h (小时), d (天), w (周), y (年) 等表示。
注意事项: 范围向量不能直接绘制成图,通常需要配合函数使用,如 rate(), increase() 等。
1 | # 获取过去 5 分钟内 http_requests_total 指标的所有样本 |
1.2 时间位移操作 (Offset)
offset 关键字用于将查询的时间点向后或向前移动。
offset 5m:查询 5 分钟前的数据。offset -1h:查询 1 小时后的数据(通常用于比较未来预测或调试)。
1 | # 查询 1 小时前 http_requests_total 的最新值 |
1.3 运算符 (Operators)
PromQL 支持各种数学、比较和逻辑运算符。
1.3.1 数学运算符
+, -, *, /, %, ^ (幂)
1 | # 计算空闲 CPU 百分比 |
1.3.2 比较运算符
==, !=, <, >, <=, >=
这些运算符可以用于即时向量和纯量之间,或两个即时向量之间。
1 | # 选择 UP 状态为 0 的实例 (服务宕机) |
当比较两个即时向量时,Prometheus 会尝试匹配两个向量中标签相同的系列。
1 | # 比较两个不同 job 的 HTTP 请求总数 |
1.3.3 逻辑/集合运算符
and, or, unless
这些运算符用于即时向量之间的集合操作。
vector1 and vector2:返回vector1中存在且在vector2中也存在(匹配标签)的元素,结果值是vector1中的值。vector1 or vector2:返回vector1中所有元素,以及vector2中不在vector1中的元素。vector1 unless vector2:返回vector1中存在但vector2中不存在(匹配标签)的元素。
1 | # 选择处于活跃状态 (up==1) 且过去 5 分钟内 HTTP 错误率超过 5% 的实例 |
二、PromQL 函数
PromQL 提供了大量的函数来处理、转换和聚合时间序列数据。
2.1 聚合函数 (Aggregators)
这些函数用于将一组即时向量聚合为更小的即时向量,通常用于在多个时间序列上进行计算。它们可以与 by 或 without 子句一起使用,以指定聚合时要保留或删除的标签。
sum(): 计算所有输入元素的和。avg(): 计算所有输入元素的平均值。min(): 计算所有输入元素的最小值。max(): 计算所有输入元素的最小值。count(): 计算所有输入元素的数量。stddev(): 计算所有输入元素的标准差。stdvar(): 计算所有输入元素的标准方差。group(): 将所有输入元素分组,并为每个组返回一个1。
by 和 without 子句:
sum(...) by (label1, label2):对指定的label1,label2进行分组,然后对每个组进行求和。结果中只保留label1,label2标签。sum(...) without (label1, label2):对除了label1,label2之外的所有标签进行分组,然后对每个组进行求和。结果中删除label1,label2标签。
1 | # 计算所有实例的 CPU 空闲时间总和 |
2.2 变化率函数 (Rate, Irates, Increase)
这些函数专门用于处理 Counter (计数器) 类型的指标,它们是 PromQL 中最常用的函数之一。
rate(v range vector): 计算一个时间窗口内,v 中时间序列的平均每秒增长率。它会自动处理计数器重置 (counter resets)。常用于计算 RPS (Requests Per Second)、BPS (Bytes Per Second) 等。1
2
3
4
5# 计算过去 5 分钟内每个 HTTP 请求路径和方法的平均 RPS
rate(http_requests_total[5m])
# 计算某个网络接口过去 1 分钟内的入站流量速率(Bytes/sec)
rate(node_network_receive_bytes_total{device="eth0"}[1m])irate(v range vector): 计算一个时间窗口内,v 中时间序列的瞬时每秒增长率。与rate相比,irate关注的是时间窗口内最后两个数据点之间的速率变化,因此对快速变化的计数器更为敏感。它也会处理计数器重置。1
2# 计算过去 5 分钟内每个 HTTP 请求路径和方法的瞬时 RPS
irate(http_requests_total[5m])何时使用
rate或irate?rate更适合用于长期趋势分析和聚合,因为它提供了更平滑的平均值。irate更适合用于快速检测瞬时峰值或下降,例如在警报规则中快速响应问题。
increase(v range vector): 计算在时间窗口内,v 中时间序列的增量。它返回一个纯量,表示计数器在该时间范围内的总增长量。1
2# 计算过去 1 小时内 HTTP 请求的总增量
increase(http_requests_total[1h])
2.3 预测与趋势函数
predict_linear(v range vector, t scalar): 基于范围向量v中时间序列的线性回归,预测在未来t秒后的值。常用于磁盘空间预测、 容量规划。1
2
3# 预测某个磁盘分区在未来 4 小时 (4 * 3600 秒) 后是否会满
# node_filesystem_avail_bytes 是可用空间
predict_linear(node_filesystem_avail_bytes{mountpoint="/data"}[1h], 4 * 3600) < 0holt_winters(v range vector, sf scalar, tf scalar): 基于 Holt-Winters 算法对范围向量v中的时间序列进行平滑和预测。sf(平滑因子)和tf(趋势因子)控制平滑程度和趋势响应速度。1
2# 使用 Holt-Winters 算法预测未来 1 小时的请求量
holt_winters(rate(http_requests_total[5m]), 0.2, 0.4)
2.4 各种实用函数
abs(v instant vector): 返回向量v中所有元素的绝对值。ceil(v instant vector): 返回向量v中所有元素向上取整的整数。floor(v instant vector): 返回向量v中所有元素向下取整的整数。round(v instant vector, to_nearest = 1 scalar): 四舍五入到最近的整数或指定的小数位。delta(v range vector): 计算范围向量v中每个时间序列的第一个和最后一个样本值之间的差值。不会处理计数器重置。deriv(v range vector): 计算范围向量v中每个时间序列的简单线性回归斜率(即每秒变化率)。vector(scalar): 将一个纯量转换为一个即时向量。1
2# 将纯量 100 转换为一个单元素的即时向量
vector(100)label_replace(v instant vector, dst_label string, replacement string, src_label string, regex string): 替换或添加标签。1
2# 将 instance 标签的值复制到 new_instance 标签中
label_replace(up{job="prometheus"}, "new_instance", "$1", "instance", "(.*)")histogram_quantile(φ scalar, b instant vector): 计算直方图的φ(0-1) 分位数。b是_bucket指标。1
2# 计算 http_request_duration_seconds 指标的 90% 分位数
histogram_quantile(0.9, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
2.5 聚合组匹配 (On/Group_left/Group_right)
当执行向量之间的操作(如加减乘除)时,两个向量的元素需要进行匹配。Prometheus 默认根据所有共享的标签进行匹配。on 和 group_left/group_right 提供了更精细的控制。
on (label1, label2):只根据指定的标签列表进行匹配。group_left()/group_right():用于在多对一或一对多的匹配中,将“多”方向量的额外标签传播到“一”方向量的结果中。
1 | # 假设有指标: |
三、PromQL 表达式评估
Prometheus 在评估 PromQL 表达式时,会根据查询的时间范围和步长 (step) 进行数据点的插值和计算。
- 即时查询 (Instant Query):返回单个时间点的数据。通常用于面板中的
Stat面板或一次性查询。 - 范围查询 (Range Query):返回一段时间序列数据。通常用于图表面板。需要指定查询范围 (end - start) 和步长 (step)。
step决定了图表上数据点的密度。
四、常见场景下的 PromQL 实践
4.1 计算服务请求速率 (RPS)
1 | # 计算每个 job 和其下每个 instance 的 HTTP 请求在过去 5 分钟内的平均 RPS |
4.2 计算错误率
1 | # 计算过去 5 分钟内,每个 job 的 5xx 错误总数 |
4.3 计算 CPU 利用率
假设 node_cpu_seconds_total 记录了每个 CPU 核心在不同模式下的秒数。
1 | # 计算过去 5 分钟内所有 CPU 核心的平均总利用率 |
4.4 统计服务在线实例数
1 | # 统计每个 job 在线的实例数量 |
4.5 内存使用率
1 | # 计算过去 5 分钟内每个 instance 的内存使用率 (Node Exporter 指标) |
或者更简单(取决于 Exporter 提供的指标):
1 | node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes``` |
4.7 警报规则示例 (YAML 文件, for Alertmanager)
1 | groups: |
五、高级用法与注意事项
- 标签的最佳实践:
- 高基数标签 (High Cardinality Labels):避免使用会产生大量唯一值作为标签的值(例如用户 ID、请求 ID),因为这会导致 Prometheus 存储大量时间序列,严重降低性能。
- 预定义的标签数量:尽量限制每个指标的标签数量,通常不超过十几个。
- 计数器重置:
rate()和increase()函数会自动处理计数器重置。不要自己手动计算两个点之间的差值,因为这会忽略重置,导致不准确的结果。 - 瞬时向量 vs. 范围向量:清楚区分两种向量类型,并了解哪些函数接受哪种类型的输入。
- CPU 使用率的计算:
rate(node_cpu_seconds_total{mode!="idle"}[5m])计算的是非空闲模式下 CPU 使用的秒数在 5 分钟内的平均每秒增长率,表示 CPU 的繁忙程度。1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))则是更标准的 Linux 系统 CPU 利用率计算方式,即1 - 空闲率。 - 分位数 (Quantiles):
histogram_quantile是计算直方图分位数的正确方式,它依赖于_bucket指标。
六、总结
PromQL 是 Prometheus 监控系统不可或缺的一部分,它的简洁性、灵活性和强大的功能使其成为处理和分析时序数据的理想工具。通过掌握指标选择器、标签匹配、运算符、以及各种内置函数,用户可以构建复杂的查询来回答关于系统性能和健康状况的各种问题,有效地发现异常,并驱动警报规则的逻辑。深入理解 PromQL 是实现高效故障排除、性能优化和容量规划的关键。
