Prometheus查询
# PromQL介绍
# 简介
PromQL(Prometheus Query Language)是Prometheus提供的一种强大的查询语言,用于从Prometheus数据库中检索时间序列数据,执行复杂的查询操作和实时数据分析。PromQL的语法包括指标选择器、操作符、函数和聚合操作。
它的作用包括如下:
数据检索:从Prometheus数据库中检索特定的时间序列数据。
数据聚合:对数据进行求和、平均、最小/最大值等聚合操作。
实时分析:提供对当前和历史数据的实时查询和分析。
告警条件定义:定义告警规则时,使用PromQL来指定触发告警的条件。
数据可视化:在Grafana等可视化工具中,使用PromQL来查询和展示数据。
# 指标类型
Prometheus支持四种基本的指标类型(Metric Types),这些类型为数据提供了丰富的语义信息,帮助用户以不同的方式理解和分析收集到的数据。这四种指标类型分别是:
# Counter(计数器)
定义:Counter是一种只会增加的指标(只在重置时归为0),一般用于存储单调递增的数据,例如:请求次数、完成的任务数、错误发生的次数等。
特点:Counter的值只能增加,重启过程中可能会重置。
# Gauge(仪表盘)
定义:Gauge是一种可以增加或减少的指标,表示一种可以随时间上升和下降的值,一般用于存储有起伏特征的数据,例如:当前温度、内存使用量等。
特点:Gauge 的值可以任意变化,增加或减少。
# Histogram(直方图)
定义:Histogram用于计算范围时间内样本的分位值,它会将样本值根据不同时间段进行划分,并以样本总数除以不同时间段的值的数量,得出其所占比例。分位值有助于了解样本数据的分布状态,例如:获得http响应时长超过10秒钟的请求所占的比例,1~9秒、10~29秒的数据各自占比多少。
特点:Histogram会将样本值根据不同时间段进行划分,并以样本总数除以不同时间段的值的数量,得出其所占比例,了解数据的分布情况。
分位数计算要使用histogram_quantile函数,该函数只是近似计算,并非太精确。
# Summary(摘要)
定义:Summary类似于Histogram,同样用于测量数据分布。但与Histogram不同,Summary由被监控端自行计算出分位数,并将结果响应给Prometheus的样本采集请求,所以其结果可以比histogram函数计算得更精确。
特点:Summary提供了更精确的分位数计算,但在分布式环境下使用Histogram更易于聚合。
# 指标选择器
# 匹配器
# 介绍
匹配器又称标签选择器,匹配器用于根据标签过滤指标数据,他的格式为:{标签名 匹配操作符 匹配值}
,多个标签用逗号隔开。
# 匹配操作符
匹配操作符用于定义匹配器的条件,以筛选满足特定条件的时间序列数据。匹配操作符有以下几种:
=
等于
- 作用:选择具有特定标签且值等于指定值的时间序列。
!=
不等于
- 作用:选择具有特定标签但值不等于指定值的时间序列。
=~
:正则匹配
- 作用:选择具有特定标签且值匹配给定正则表达式的时间序列。
!~
:正则不匹配
- 作用:选择具有特定标签但值不匹配给定正则表达式的时间序列。
# 使用示例
{method="GET"}
选择所有method
标签值为"GET"
的http_requests_total
时间序列。
http_requests_total{path=~"/api/.*"}
选择path
标签值匹配正则表达式"/api/.*"
的http_requests_total
时间序列。
# 注意事项
标签值为空的特殊情况:如果使用匹配器时指定标签值为空,如{env=""}
,则Prometheus会选择没有该标签或该标签值为空的时间序列。可以用来筛选出没有某个标签的时间序列。
匹配操作的性能考虑:使用正则表达式匹配操作时,应该注意正则表达式的复杂性,过于复杂的正则表达式会对查询性能产生影响。
匹配器的逻辑组合:在同一个选择器中使用多个匹配器时,它们之间的关系是逻辑与(AND),即只有同时满足所有匹配条件的时间序列才会被选择。
# 即时向量选择器
# 介绍
即时向量选择器用于选择当前时间点的一组时间序列数据。由指标名称和匹配器(标签选择器)组成。我们可以使用指标名过滤指标数据,也可以使用匹配器根据标签过滤指标数据,亦或者两者都使用。
向量选择器至少要包含一个指标名称,或至少一个不匹配空字符串的匹配器。
例如:
{mode=""}
,仅使用了匹配空字符串的匹配器,因此是非法的选择器。
# 使用示例
选择指定指标的所有时间序列:
http_requests_total
这个查询会选择指标名为http_requests_total
的所有时间序列。
使用等于匹配器选择特定标签的时间序列:
http_requests_total{method="GET", status="200"}
这个查询会选择方法为GET、状态为200的http_requests_total
指标的时间序列。
使用不匹配正则表达式匹配器:
http_requests_total{status!~"5.."}
这个查询会选择状态码不以5开头的http_requests_total
指标的时间序列。
# 范围向量选择器
# 介绍
范围向量选择器在PromQL中用于选择一段时间范围内的时间序列数据,而不仅仅是某一个具体时刻的数据,这对于计算速率、平均值等时间序列分析非常重要。
# 时间范围
范围向量选择器通过在即时向量选择器的基础上添加方括号[]
来定义,并在方括号内指定要查询的时间范围。时间范围定义了要查询的时间段长度,从查询执行的当前时间点向过去回溯。
支持的时间单位包括:
ms
(毫秒)s
(秒)m
(分钟)h
(小时)d
(天)w
(周)y
(年)
在指定时间范围时,必须使用整数值,可以组合使用不同的时间单位来表示精确的时间范围,时间单位按照从大到小的顺序组合使用。
有效的示例:
1h30m
(1小时30分钟)、10m15s
(10分钟15秒)。无效的示例:不能使用如
1.5h
(这表示1小时30分钟,但格式不正确)。
# 使用示例
范围向量选择器通常用于对过去一段时间内的数据进行分析,所以会搭配各种函数进行使用。
假设想计算过去5分钟内HTTP请求的速率,可以使用如下查询:
rate(http_requests_total[5m])
这个查询使用了rate()
函数和一个范围向量选择器[5m]
来计算过去5分钟内每秒的HTTP请求增加量的平均值。
# 偏移量修改器
# 介绍
偏移量修改器(offset modifier)在PromQL中用于对查询的基准时间点进行调整,从而能够访问过去某个具体时刻的数据。这在分析历史数据趋势、进行时间对比分析时特别有价值。
# 使用方法
偏移量修改器通过在向量选择器或者指标查询后添加offset
关键字及相应的时间值,从而将基准时间点从现在调整至向前指定一段时间值。
- 在向量选择器中使用
offset
即时向量选择器:
# 查询将返回2小时前的可用内存量的即时样本 node_memory_MemAvailable_bytes offset 2h
1
2范围向量选择器:
# 该查询将返回一天前此时的5分钟内所有状态码为200且处理程序匹配/static.*的HTTP请求的即时样本 prometheus_http_requests_total{code="200",handler=~"/static.*"}[5m] offset 1d
1
2
- 在指标名称后直接使用
offset
# 这个查询将计算5分钟前所有CPU时间的总和
sum(node_cpu_seconds_total offset 5m)
2
# 聚合表达式
# 介绍
按照某种分类机制进行分组,并将查询结果按组进行聚合计算是较为常见的需求,例如分组求平均数/求和/总数等。
PromQL的聚合表达式允许对一组时间序列数据进行分组、然后进行聚合计算,并返回单个值或几个值作为结果,这些表达式主要用于数据汇总、统计分析和性能指标的监控。
promQL会先分组、然后才会进行聚合函数计算。
聚合函数与分组只能对即时向量使用,范围向量无法使用。
# 分组子句
# by子句
by
子句用于将指定标签作为分组标准。结果中未被子句指定的标签会被忽略。
# 表达式计算所有状态码为 200 的 HTTP 请求,并按method标签进行分组求和
sum(http_requests_total{code="200"}) by (method)
2
# without子句
功能与by
相反,without
子句用于将指定标签进行排除,未指定的标签则用作分组标准。结果中被子句指定的标签会被忽略。
# 这个表达式计算所有状态码为200的HTTP请求,并在聚合时忽略instance标签,即所有instance相关的时间序列数据会合并计算
sum(http_requests_total{code="200"}) without (instance)
2
# 聚合函数
聚合函数用于对分组内的时间序列进行计算,并返回聚合后的结果。
sum()
:对样本值求和。avg()
:对样本值求平均值。count()
:对分组内的时间序列数量进行统计。min()
和max()
:求取样本值中的最小者和最大者。stddev()
和stdvar()
:对样本值求标准差和方差,帮助了解数据的波动大小。topk()
和bottomk()
:返回指标样本值中,数值最大和最小的前n的时间序列,例如:topk(3, prometheus_http_requests_total)
quantile()
:用于计算一组时间序列样本值的分位数,评估数据的分布状态,该函数会返回分组内指定的分位数的值,即数值落在小于等于指定的分位区间的比例。count_values()
:对一组时间序列的样本值进行计数,统计每个值出现的次数。
# 非聚合函数
非聚合函数处理向量数据,不涉及分组聚合操作。
rate()
:计算时间序列在给定时间范围内的平均增长率,常用于Counter类型的指标,例如:rate(prometheus_http_requests_total{code="200"}[5m])
irate()
:比rate()精细度更高,可计算指标的瞬时速率。irate会将采集到的最后2个指标,新指标减去旧指标,再除以两个指标经过的秒数。例如:irate(prometheus_http_requests_total{code="200"}[5m])
predict_linear(v range-vector, t, scalar)
:用于预测时间序列v在t秒后的值,他通过线性回归的方式来预测样本数据的Gauge变化趋势。delta(v range-vector)
:计算范围向量中的每个时间序列的第一个值和最后一个值之差,从而展示不同时间点上的样本值的差值,它不考虑中间的波动,只关注起止点的差异。例如:delta(prometheus_http_requests_total{code="200", handler=~"/graph.*"}[5m])
,会返回该条件的HTTP请求数现在与5分钟前的变化差异。
# 向量匹配
# 向量匹配介绍
向量匹配用于执行二元运算时定义如何匹配两个向量的元素。在执行二元运算时,如果是两个即时向量之间的运算,就必须进行向量匹配。而即时向量与标量之间的运算则不需要显式的向量匹配。
PromQL支持以下几种向量匹配方式:
- 一对一匹配:当两个向量的每个元素都严格匹配时进行运算。
一对一向量匹配是PromQL中二元运算的基础,允许对左右两个向量中具有相同标签集的样本进行运算。这种匹配方式适用于标签集完全一致的情况,确保了运算的准确性和有意义性。
- 多对一和一对多匹配:多对一指当左向量中多个元素与右向量中的单个元素匹配时进行运算。一对多则相反。
多对一或一对多向量匹配时,需要使用
group_left
或group_right
子句来指定哪边可以有多个匹配项。同时需要确保使用
group_left
或group_right
的一侧是多个匹配项,另一侧只有一个匹配项,否则会出现运算错误。
# 向量匹配子句
# on子句
on
子句用于指定向量匹配时需要匹配的标签。只有在on
子句中指定的标签完全匹配时,两个向量的样本才会进行运算。格式:[向量表达式1] [二元运算符] on(指定的标签) [向量表达式2]
# ignoring子句
ignoring
子句与on
相反,用于指定在向量匹配时应忽略的标签。使用ignoring
可以在两个向量的标签集不完全一致时进行运算,忽略掉不需要匹配的标签。格式:[向量表达式1] [二元运算符] ignoring(指定的标签) [向量表达式2]
# group_left子句
用于多对一匹配,表示左向量可以有多个样本匹配到右向量的单个样本。[向量表达式1] [二元运算符] on/ignoring(指定的标签) group_left(额外标签) [向量表达式2]
# group_right子句
用于一对多匹配,表示右向量可以有多个样本匹配到左向量的单个样本。格式:[向量表达式1] [二元运算符] on/ignoring(指定的标签) group_right [向量表达式2]
使用
group_left
或group_right
时,可以在这些子句后指定额外标签。可以将指定标签从"多"的一侧传递到结果向量中,以便保留重要的上下文信息。当然也可以不指定,不传递任何额外标签。
# 使用示例
# 一对一,忽略job标签,将内存可用字节数和CPU使用秒数相加
node_memory_MemAvailable_bytes + ignoring(job) node_cpu_seconds_total
# 多对一,当instance标签相匹配时,左边的可用内存数和右边的CPU使用秒数才进行乘法运算
node_memory_MemAvailable_bytes{instance="localhost:9100"} * on(instance) group_left node_cpu_seconds_total
# 多对一, 表示node_cpu_seconds_total中每个样本都可以根据instance标签与node_memory_MemAvailable_bytes中的相应样本进行乘法运算
node_cpu_seconds_total * on(instance) group_left(job) node_memory_MemAvailable_bytes
# 多对一,当instance标签相匹配时,左边的可用内存数和右边的CPU使用秒数才进行乘法运算
node_memory_MemAvailable_bytes{instance="localhost:9100"} * on(instance) group_left node_cpu_seconds_total
2
3
4
5
6
7
8
9
10
11
# 现实例子
# 一对一向量匹配
想象一下你在管理一家连锁咖啡店,每家店铺每天的营业额是你关心的数据。现在,你有两张表格,一张记录了昨天每家店铺的营业额(coffee_shop_sales_yesterday),另一张记录了今天每家店铺的营业额(coffee_shop_sales_today)。如果你想计算出每家店铺今天相比昨天营业额的增长额,你就需要进行一对一的匹配:每家店铺今天的营业额减去昨天的营业额。这里,"每家店铺"就是匹配的关键标签,保证了我们准确匹配了同一家店铺的数据。
该场景中,我们现在有两个指标:coffee_shop_sales_yesterday
和coffee_shop_sales_today
,分别记录了昨天和今天每家咖啡店的营业额。我们想要计算每家店铺今天相比昨天营业额的增长额。如果每家店铺由标签shop_id
唯一标识,那么相应的PromQL表达式可能是这样的:
# 这个表达式使用了-运算符和on(shop_id)来确保只有当shop_id完全匹配时,才将今天的营业额与昨天的营业额进行相减操作。这是一对一匹配的典型例子,每家店铺的今日营业额都会减去其昨日营业额
coffee_shop_sales_today - on(shop_id) coffee_shop_sales_yesterday
2
# 多对一向量匹配
假设在一个学校中,每个班级有一个班主任和多名学生。我们想要计算每个班级学生的总成绩,并将班主任的名字标记在最终的结果上,以便知道每个班级及其对应班主任的学生总成绩。这里,一个班主任和多名学生之间的关系构成了多对一的向量匹配——每个班级对应一个班主任和多名学生。我们的目标是计算每个班级学生的总成绩,并在结果中保留班主任的信息。
该场景中,我们假设有如下数据:
student_score{class="1A", student="学生A"}
:记录1A班学生A的成绩,其他学生同理。teacher{class="1A", teacher="王老师"}
:记录1A班班主任的名字,假设每个班级只有一个班主任,这个指标的值可以是任意的,因为我们关心的是标签信息。
我们可以使用sum
函数来聚合每个班级学生总成绩,然后利用group_left
来匹配并保留班主任的信息:
sum(student_score) by (class) * on(class) group_left(teacher) teacher
这个表达式的解释如下:
sum(student_score) by (class)
:按班级对学生的成绩进行求和。* on(class) group_left(teacher) teacher
:使用乘法运算符*
(在这里作为一种技巧来应用标签而不是实际计算乘法)基于class
进行向量匹配,并利用group_left
保留teacher
标签。这样,尽管teacher
指标的值可能不直接参与计算,它的标签信息(即班主任的名字)会被保留在最终的聚合结果中。
# 二元运算符
PromQL支持基本的算术运算、比较运算和逻辑运算。
- 算术运算符:+(加)、-(减)、*(乘)、/(除)、%(取模)、^(取幂)
- 比较运算符:==、!=、>、<、>=、<=
- 逻辑运算符:and(并且)、or(或者)、unless(除了)
# 运算符优先级
运算符优先级可通过括号改变运算优先级,高优先级运算符先计算,同优先级从左向右依次计算。
- ^
- *,/,%
- +,-
- ==,!=,<=,>=,>,<
- and,unless
- or