你好,我是你的老朋友,监控达人“Prometheus小能手”。今天咱们来聊聊PromQL的那些高级玩法,保证让你对PromQL的理解更上一层楼!
前言:PromQL,不仅仅是查询
对于咱们SRE工程师来说,Prometheus就像是战场上的“鹰眼”,帮我们洞察一切。而PromQL,则是我们与Prometheus沟通的“密语”。别以为PromQL只是简单的查询,它其实蕴藏着强大的数据分析和告警能力。掌握PromQL的高级用法,能让你在监控的道路上事半功倍。
在之前的学习中,你可能已经熟悉了PromQL的基础查询,比如指标选择器、范围向量、瞬时向量等等。但这些只是冰山一角。今天,咱们就来揭开PromQL的神秘面纱,看看它还有哪些“隐藏技能”。
核心技能一:聚合操作(Aggregation)—— 数据汇总的艺术
想象一下,你现在要统计所有服务的平均CPU使用率,或者计算某个接口的99th分位延迟,怎么办?这时候,聚合操作就派上用场了。
PromQL提供了丰富的聚合函数,可以对数据进行各种维度的汇总。常见的聚合函数包括:
sum()
:求和avg()
:求平均值min()
:求最小值max()
:求最大值count()
:计数stddev()
:求标准差stdvar()
:求方差quantile(φ, expr)
:计算分位数(φ的取值范围是0到1)topk(k, expr)
:取样本值最大的k个bottomk(k, expr)
:取样本值最小的k个
聚合的维度:by
和 without
聚合操作通常需要指定一个或多个维度,PromQL中使用by
和without
子句来实现。
by
:表示按照指定的维度进行分组聚合。without
:表示排除指定的维度,按照剩余的维度进行分组聚合。
举个例子:
# 计算所有服务的平均CPU使用率
avg(node_cpu_seconds_total{mode="idle"}) without (cpu)
# 按照instance和job维度计算CPU使用率的总和
sum(node_cpu_seconds_total{mode="idle"}) by (instance, job)
思考: 为什么要区分by
和without
?它们各自的适用场景是什么?
by
和 without
的设计,让我们可以更灵活地控制聚合的维度。by
适用于明确知道要按照哪些维度分组的情况,而without
适用于只关心排除某些维度,而对剩余维度不关心的情况。 在性能方面,without
通常比 by
更高效,因为它只需要排除少量维度。
进阶用法:多重聚合
有时候,我们需要进行多重聚合,比如先按照instance分组求和,再计算所有instance的平均值。PromQL支持这种嵌套的聚合操作。
# 先按照instance分组求和,再计算所有instance的平均CPU使用率
avg(sum(node_cpu_seconds_total{mode="idle"}) by (instance))
注意: 多重聚合的顺序很重要,不同的顺序可能会得到不同的结果。
核心技能二:子查询(Subquery)—— 时间的切片
子查询是PromQL 2.7版本引入的一个强大特性。它允许我们在一个查询中嵌套另一个查询,实现更复杂的数据分析。
子查询的基本语法是:<表达式> [时间范围]
其中,时间范围用方括号[]
表示,可以是一个绝对时间范围,也可以是一个相对时间范围。
子查询的应用场景
计算过去一段时间内的平均值、最大值、最小值等。
# 计算过去1小时内CPU使用率的平均值 avg_over_time(node_cpu_seconds_total{mode="idle"}[1h])
计算速率的变化率。
# 计算过去5分钟内请求速率的变化率 rate(http_requests_total[5m]) - rate(http_requests_total[10m])
结合
quantile_over_time
计算分位数
子查询和quantile_over_time
组合可以用于计算一段时间内指标的分位数值。quantile_over_time(0.9, http_request_duration_seconds[1h])
更复杂的计算
假设你需要计算过去1小时内CPU使用率的平均值,并且只考虑CPU使用率大于0.5的时间点。 这时候可以结合子查询做更复杂的计算。avg_over_time((node_cpu_seconds_total{mode="idle"} > 0.5)[1h:])
注意: 子查询的时间范围必须是方括号[]
括起来,并且不能包含offset。
核心技能三:直方图(Histogram)—— 数据的分布
直方图是PromQL中一种特殊的指标类型,用于表示数据的分布情况。它可以帮助我们了解数据的集中趋势、离散程度等信息。
Prometheus的直方图是累积直方图(Cumulative Histogram)。累积直方图由一系列的bucket(桶)组成,每个bucket包含一个上限值和一个计数器。计数器的值表示小于等于该上限值的样本数量。
直方图的指标
直方图类型的指标通常包含以下三个子指标:
<basename>_bucket{le="<upper inclusive bound>"}
:表示小于等于上限值的样本数量。<basename>_sum
:表示所有样本值的总和。<basename>_count
:表示所有样本的总数。
例如,http_request_duration_seconds
这个直方图指标,就包含了http_request_duration_seconds_bucket
、http_request_duration_seconds_sum
和http_request_duration_seconds_count
三个子指标。
直方图的函数
PromQL提供了一些专门用于处理直方图的函数:
histogram_quantile(φ, b)
:计算分位数(φ的取值范围是0到1,b是bucket)。histogram_sum(b)
:计算所有样本值的总和。histogram_count(b)
:计算所有样本的总数。
如何使用直方图计算分位数
# 计算http_request_duration_seconds的99th分位延迟
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
注意: histogram_quantile
函数计算出的分位数是近似值,精度取决于bucket的划分。
性能优化:让你的查询飞起来
PromQL的性能优化是一个永恒的话题。以下是一些常用的优化技巧:
- 合理使用标签(Label):标签是PromQL的灵魂,但过多的标签会增加查询的负担。尽量减少不必要的标签,只保留真正需要的维度。
- 避免使用高基数(High Cardinality)的标签:高基数标签指的是具有大量不同值的标签,比如用户ID、IP地址等。这类标签会导致Prometheus存储大量的时序数据,严重影响查询性能。如果必须使用高基数标签,可以考虑将其拆分成多个低基数标签。
- 使用记录规则(Recording Rules):对于一些常用的、复杂的查询,可以将其定义为记录规则。记录规则会预先计算结果并存储为新的时序数据,从而加快查询速度。
- 合理设置查询范围:查询范围越大,需要扫描的数据就越多,查询速度就越慢。尽量缩小查询范围,只查询必要的数据。
- 使用
rate()
函数时,注意范围向量的选择器时长至少是抓取间隔的4倍:这是为了避免“长尾效应”对速率计算的影响。 - 避免在
rate()
函数中使用sum()
: 这样做通常是错误的,因为rate()
函数本身已经包含了对时间窗口内样本的平均。 - 使用
up
指标检查目标是否存活:up
指标是一个特殊的指标,用于表示目标的健康状态。可以利用up
指标快速判断目标是否存活,避免不必要的查询。
告警实战:让Prometheus成为你的“哨兵”
PromQL不仅可以用于查询和分析数据,还可以用于定义告警规则。Prometheus的告警规则基于PromQL表达式,当表达式的值满足特定条件时,就会触发告警。
告警规则的组成
一个告警规则通常包含以下几个部分:
alert
:告警名称。expr
:PromQL表达式,用于计算告警条件。for
:持续时间,表示告警条件持续多长时间才触发告警。labels
:附加的标签,用于标识告警的严重程度、类型等信息。annotations
:附加的注释,用于描述告警的详细信息、处理建议等。
一个简单的告警规则示例
alert: HighRequestLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: critical
annotations:
summary: "High request latency"
description: "The 99th percentile request latency is above 1 second for 5 minutes."
这个告警规则表示:当http_request_duration_seconds
的99th分位延迟超过1秒,并且持续5分钟以上,就会触发一个名为HighRequestLatency
的告警,告警级别为critical
。
总结:PromQL,监控的利器
今天,我们一起学习了PromQL的高级用法,包括聚合操作、子查询、直方图以及性能优化和告警实战。相信你对PromQL的理解又加深了一层。
PromQL是一个强大而灵活的工具,掌握它可以让你更好地理解和分析监控数据,及时发现和解决问题。希望今天的分享能对你有所帮助,让Prometheus成为你监控的得力助手!
如果你在使用PromQL的过程中遇到任何问题,或者有什么好的想法和建议,欢迎随时与我交流。我是“Prometheus小能手”,我们下期再见!
附录:PromQL常用函数一览表
函数 | 描述 | 适用类型 |
---|---|---|
abs(v instant-vector) |
返回v中所有样本的绝对值 | 瞬时向量 |
ceil(v instant-vector) |
返回v中所有样本值向上取整 | 瞬时向量 |
floor(v instant-vector) |
返回v中所有样本值向下取整 | 瞬时向量 |
round(v instant-vector, to_nearest=1) |
返回v中所有样本值四舍五入到最近的整数,to_nearest参数可选,表示四舍五入的精度 | 瞬时向量 |
changes(v range-vector) |
返回v中每个时间序列在时间范围内的变化次数 | 范围向量 |
clamp_max(v instant-vector, max scalar) |
将v中所有样本值限制在max以下 | 瞬时向量 |
clamp_min(v instant-vector, min scalar) |
将v中所有样本值限制在min以上 | 瞬时向量 |
day_of_month(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在月份的第几天(1-31) | 瞬时向量(可选) |
day_of_week(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在周的第几天(0-6,0表示周日) | 瞬时向量(可选) |
days_in_month(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在月份的天数(28-31) | 瞬时向量(可选) |
delta(v range-vector) |
计算范围向量v中每个时间序列的第一个值和最后一个值的差值 | 范围向量 |
deriv(v range-vector) |
计算范围向量v中每个时间序列的导数(基于线性回归) | 范围向量 |
exp(v instant-vector) |
计算v中所有样本值的指数(以e为底) | 瞬时向量 |
histogram_quantile(φ scalar, b instant-vector) |
计算直方图b中φ分位数的值(φ的取值范围是0到1) | 瞬时向量 |
holt_winters(v range-vector, sf scalar, tf scalar) |
计算范围向量v中每个时间序列的Holt-Winters预测值(sf是平滑因子,tf是趋势因子) | 范围向量 |
hour(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在小时(0-23) | 瞬时向量(可选) |
idelta(v range-vector) |
计算范围向量v中每个时间序列的最后两个值的差值 | 范围向量 |
increase(v range-vector) |
计算范围向量v中每个时间序列的增长量(考虑计数器重置) | 范围向量 |
irate(v range-vector) |
计算范围向量v中每个时间序列的最后两个值的瞬时增长率 | 范围向量 |
label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) |
对于v中的每个时间序列,使用regex匹配src_label的值,并将匹配到的部分替换为replacement,然后将结果写入dst_label中。如果未匹配到,则不进行替换。 | 瞬时向量 |
label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...) |
将v中每个时间序列的多个src_label的值使用separator连接起来,并将结果写入dst_label中。 | 瞬时向量 |
ln(v instant-vector) |
计算v中所有样本值的自然对数 | 瞬时向量 |
log10(v instant-vector) |
计算v中所有样本值的以10为底的对数 | 瞬时向量 |
log2(v instant-vector) |
计算v中所有样本值的以2为底的对数 | 瞬时向量 |
minute(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在分钟(0-59) | 瞬时向量(可选) |
month(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在月份(1-12) | 瞬时向量(可选) |
predict_linear(v range-vector, t scalar) |
基于范围向量v中每个时间序列的线性回归,预测t秒后的值 | 范围向量 |
rate(v range-vector) |
计算范围向量v中每个时间序列的平均每秒增长率(考虑计数器重置) | 范围向量 |
resets(v range-vector) |
返回v中每个时间序列在时间范围内的重置次数 | 范围向量 |
scalar(v instant-vector) |
如果v只有一个时间序列,则返回该时间序列的样本值作为标量;否则,返回NaN | 瞬时向量 |
sort(v instant-vector) |
对v中的时间序列按照样本值升序排序 | 瞬时向量 |
sort_desc(v instant-vector) |
对v中的时间序列按照样本值降序排序 | 瞬时向量 |
sqrt(v instant-vector) |
计算v中所有样本值的平方根 | 瞬时向量 |
time() |
返回当前UTC时间的时间戳(秒) | 无 |
timestamp(v instant-vector) |
返回v中每个样本的时间戳(秒) | 瞬时向量 |
vector(s scalar) |
将标量s转换为一个没有标签的向量 | 无 |
year(v=vector(time()) instant-vector) |
返回UTC时间中每个样本值所在年份 | 瞬时向量(可选) |
注意: 这只是PromQL常用函数的一部分,更详细的函数列表请参考Prometheus官方文档。