HOOOS

PromQL高级进阶:聚合、子查询、直方图与性能优化实战指南

0 83 Prometheus小能手 PrometheusPromQL监控
Apple

你好,我是你的老朋友,监控达人“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个

聚合的维度:bywithout

聚合操作通常需要指定一个或多个维度,PromQL中使用bywithout子句来实现。

  • 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)

思考: 为什么要区分bywithout?它们各自的适用场景是什么?

bywithout 的设计,让我们可以更灵活地控制聚合的维度。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. 计算过去一段时间内的平均值、最大值、最小值等。

    # 计算过去1小时内CPU使用率的平均值
    avg_over_time(node_cpu_seconds_total{mode="idle"}[1h])
    
  2. 计算速率的变化率。

    # 计算过去5分钟内请求速率的变化率
    rate(http_requests_total[5m]) - rate(http_requests_total[10m])
    
  3. 结合quantile_over_time计算分位数
    子查询和quantile_over_time组合可以用于计算一段时间内指标的分位数值。

     quantile_over_time(0.9, http_request_duration_seconds[1h])
    
  4. 更复杂的计算
    假设你需要计算过去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_buckethttp_request_duration_seconds_sumhttp_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的性能优化是一个永恒的话题。以下是一些常用的优化技巧:

  1. 合理使用标签(Label):标签是PromQL的灵魂,但过多的标签会增加查询的负担。尽量减少不必要的标签,只保留真正需要的维度。
  2. 避免使用高基数(High Cardinality)的标签:高基数标签指的是具有大量不同值的标签,比如用户ID、IP地址等。这类标签会导致Prometheus存储大量的时序数据,严重影响查询性能。如果必须使用高基数标签,可以考虑将其拆分成多个低基数标签。
  3. 使用记录规则(Recording Rules):对于一些常用的、复杂的查询,可以将其定义为记录规则。记录规则会预先计算结果并存储为新的时序数据,从而加快查询速度。
  4. 合理设置查询范围:查询范围越大,需要扫描的数据就越多,查询速度就越慢。尽量缩小查询范围,只查询必要的数据。
  5. 使用rate()函数时,注意范围向量的选择器时长至少是抓取间隔的4倍:这是为了避免“长尾效应”对速率计算的影响。
  6. 避免在rate()函数中使用sum(): 这样做通常是错误的,因为rate()函数本身已经包含了对时间窗口内样本的平均。
  7. 使用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官方文档。

点评评价

captcha
健康