2.5.1.4 逻辑二元操作符(Logical/set binary operators)
逻辑二元操作符如下:
- and: (intersection)
- or: (union)
- unless:(complement)
2.5.2 向量匹配(Vector matching)
向量之间的运算试图为左边的每一个元素在右边的向量中找到一个匹配的元素。匹配行为有两种基本类型:一对一和多对一/一对多。
2.5.2.1 一对一向量匹配(One-to-one vector matches)
一对一从操作的每一边找到一个唯一的条目对。在默认情况下,这是一个遵循vector1 <操作符> vector2
格式的操作。
如果两个条目具有完全相同的标签集和相应的值,则它们是匹配的。ignore
关键字允许在匹配时忽略某些标签,而on
关键字允许将考虑的标签集缩减为所提供的列表:
<vector expr> <bin-op> ignoring(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) <vector expr>
input举例:
method_code:http_errors:rate5m{method="get", code="500"} 24 method_code:http_errors:rate5m{method="get", code="404"} 30 method_code:http_errors:rate5m{method="put", code="501"} 3 method_code:http_errors:rate5m{method="post", code="500"} 6 method_code:http_errors:rate5m{method="post", code="404"} 21 method:http_requests:rate5m{method="get"} 600 method:http_requests:rate5m{method="del"} 34 method:http_requests:rate5m{method="post"} 120
query举例:
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
举例:下面将返回一个结果向量,其中包含每个方法在过去5分钟内的状态码为500的HTTP请求的比例。如果不忽略(代码),就不会有匹配,因为度量标准不共享相同的标签集。带有put和del方法的条目没有匹配,不会在结果中显示:
{method="get"} 0.04 // 24 / 600 {method="post"} 0.05 // 6 / 120
2.5.2.2 多对一和一对多向量匹配(Many-to-one and one-to-many vector matches)
多对一和一对多匹配指的是“一”面上的每个向量元素都可以与“多”面上的多个元素匹配。这必须使用group_left或group_right修饰符显式地请求,其中left/right决定哪个向量具有更高的基数。
<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr> <vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr> <vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr>
与组修饰符一起提供的标签列表包含了要包含在结果度量中的来自“一侧”的附加标签。在一个标签上只能出现在一个列表中。结果向量的每个时间序列必须是唯一可识别的。
分组修饰符只能用于比较和算术。操作as和,除非和或默认匹配右向量中的所有可能条目。
示例查询:
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
在这种情况下,左向量每个方法标签值包含多个条目。因此,我们使用group_left来表示这一点。右边的元素现在与左边的多个具有相同方法标签的元素相匹配:
{method="get", code="500"} 0.04 // 24 / 600 {method="get", code="404"} 0.05 // 30 / 600 {method="post", code="500"} 0.05 // 6 / 120 {method="post", code="404"} 0.175 // 21 / 120
多对一和一对多匹配是应该仔细考虑的高级用例。通常,正确使用ignoring(<labels>)
会提供所需的结果。
2.5.3 聚合运算符(Aggregation operators)
Prometheus
支持以下内置的聚合操作符,可用于聚合单个瞬时向量的元素,从而生成一个具有聚合值的元素更少的新向量:
- sum (calculate sum over dimensions)
- min (select minimum over dimensions)
- max (select maximum over dimensions)
- avg (calculate the average over dimensions)
- group (all values in the resulting vector are 1)
- stddev (calculate population standard deviation over dimensions)
- stdvar (calculate population standard variance over dimensions)
- count (count number of elements in the vector)
- count_values (count number of elements with the same value)
- bottomk (smallest k elements by sample value)
- topk (largest k elements by sample value)
- quantile (calculate φ-quantile (0 ≤ φ ≤ 1) over dimensions)
这些操作符既可以用于对所有标签维度进行聚合,也可以通过包含without
或by
子句来保留不同的维度。这些子句可以在表达式之前或之后使用。
<aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>) • 1
或者
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)] • 1
2.5.4 二元操作符优先级(Binary operator precedence)
下面的列表显示了二元制操作符在普罗米修斯中从高到低的优先级:
^
*, /, %, atan2
+, -
==, !=, <=, <, >=, >
and, unless
or
具有相同优先级的操作符是左结合的。例如,2 * 3% % 2
等于(2 * 3)% 2
。然而^
是右结合律,所以2 ^ 3 ^ 2
等于2 ^ (3 ^ 2)
2.6 函数(Functions)
Prometheus
支持对数据进行操作的几个函数,详情参考:https://prometheus.io/docs/prometheus/latest/querying/functions/
函数名 | 解析 |
abs() | 求瞬时向量绝对值 |
absent() | 传入一个瞬时非空向量则返回空向量,否则返回不带名称值为1的指标,用来监控空数据的情况,即nodata监控 |
absent_over_time() | 返回一个空向量;如果传递给它的范围向量没有元素,则返回一个1元素的值为1的向量。 |
ceil() | 四舍五入取整 |
changes() | 计算区间向量中每个样本值变化次数,返回的是瞬时向量 |
clamp() | 所有元素的样本值,使其下限为min,上限为max。 |
clamp_max() | 瞬时向量值如果大于max 则值修改为max,否则值不变(用于将向量规范到一个不大于max的区间内) |
clamp_min() | 同理,将向量规范到一个不小于min值的范围内 |
day_of_month() | 返回 1~31 向量所在UTC时间中的所在月份的第几天 |
day_of_week() | 返回被给定 UTC 时间所在周的第几天。返回值范围:0~6,0 表示星期天。 |
days_in_month() | 返回当月一共有多少天。返回值范围:28~31。 |
delta() | 参数是一个区间向量,返回一个瞬时向量。它计算一个区间向量 v 的第一个元素和最后一个元素之间的差值(和张羽看了prometheus的实现代码,可以简单理解为就是最后一个点的值减去第一个点的值。但是实际上真实场景中,第一个点和最后一个点不可能每次都正好每和时间区间的第一个时间点和最后一个时间点相吻合,所以prometheus在代码中做了大量的计算,由于涉及到很多除法运算,因此结果一般都是小数) |
deriv() | 参数是一个区间向量,返回一个瞬时向量。它使用简单的线性回归计算区间向量 v 中各个时间序列的导数。 |
exp() | 输入一个瞬时向量,返回各个样本值的 e 的指数值,即 e 的 N 次方。当得到一个无穷大的值,显示 +Inf, 反之显示0, e的负数次方无限趋进0。e的空向量指数依然为空向量。 |
floor() | 与 ceil() 函数相反,舍弃小数部分取整 |
histogram_quantile() | 从直方图的桶b计算φ-分位数(0≤φ≤1)。 |
holt_winters() | 生成一个基于v的范围的时间序列的平滑值,平滑因子sf越低,旧数据越重要。趋势因子tf越高,数据中考虑的趋势越多。sf和tf都必须在0和1之间。 |
hour() | 函数返回被给定 UTC 时间的当前第几个小时,时间范围:0~23。 |
idelta() | 参数是一个区间向量, 返回一个瞬时向量。它计算最新的 2 个样本值之间的差值。(当区间内仅有一个向量时无返回值) |
increase() | 函数获取区间向量中的第一个和最后一个样本并返回其增长量,它会在单调性发生变化时(如由于采样目标重启引起的计数器复位)自动中断。 |
irate() | 使用rate计算快速变化的样本平均增长率时,容易陷入长尾问题,因为它用平均值将峰值削平了,无法反映时间窗口内样本数据的快速变化。与rate类似,irate同样可以计算counter的平均增长率,但其反映出的是瞬时增长率。irate计算增长率时,使用指定时间范围内的最后两个样本数据 |
label_join() | 函数可以将时间序列 v 中多个标签 src_label 的值,通过 separator 作为连接符写入到一个新的标签 dst_label 中。可以有多个 src_label 标签 |
label_replace() | 在原标签中匹配字符,放到目标标签中 |
ln() | 计算瞬时向量 v 中所有样本数据的自然对数。 |
log2() | 计算瞬时向量 v 中所有样本数据的二进制对数。特殊情况同ln |
log10() | 计算瞬时向量 v 中所有样本数据的十进制对数。特殊情况同ln。 |
minute() | 函数返回给定 UTC 时间当前小时的第多少分钟。结果范围:0~59。 |
month() | 函数返回给定 UTC 时间当前属于第几个月,结果范围:0~12。 |
predict_linear() | 可以预测时间序列 v 在 t 秒后的值。它基于简单线性回归的方式,对时间窗口内的样本数据进行统计,从而可以对时间序列的变化趋势做出预测。 |
rate() | 用于计算区间向量平均增长率,如计算QPS |
resets() | 对于每个时间序列,它都返回一个计数器重置的次数。两个连续样本之间单调性发生变化被认为是一次计数器重置。 |
round() | 与 ceil 和 floor 函数类似,返回向量中所有样本值的最接近to_nearest值整数倍的值,当to_nearest=1时等价于ceil。 |
scalar() | 返回一个单元素瞬时向量的样本值,当多元素或者没有元素返回Nan。 |
sgn() | 返回一个向量,所有样本值都转换为它们的符号,定义为:1如果v是正的,-1如果v是负的,0如果v等于0。 |
sort() | 对向量按元素的值进行升序排序。 |
sort_desc() | 对向量按元素的值进行降序排序。 |
sqrt() | 计算向量 v 中所有元素的平方根。 |
time() | 返回自UTC 1970年1月1日以来的秒数。请注意,这实际上并不返回当前时间,而是表达式被求值的时间。 |
timestamp() | 返回向量 v 中的每个样本的时间戳(从 1970-01-01 到现在的秒数)。 |
vector() | 将标量 s 作为没有标签的向量返回(和scalar作用刚好相反)。 |
year() | 函数返回被给定 UTC 时间的当前年份。 |
<aggregation>_over_time() |
允许在一段时间内聚合给定范围向量的每个序列,并返回带有每个序列聚合结果的即时向量 |
2.7 注释(Comments)
PromQL 支持以#
开头的注释, 例如:
# This is a comment
2.8 陷阱(Gotchas)
2.8.1 过时(Staleness)
这里主要声明一些关于过期的描述,直接贴图:
2.8.2 避免慢查询和重载(Avoiding slow queries and overloads)
这里主要讲慢查询和重载的注意点,直接贴图:
03 举例
3.1 简单的时间序列选择(Simple time series selection)
返回带有度量http_requests_total
的所有时间序列:
http_requests_total
返回带有指标http_requests_total和给定的作业和处理器标签的所有时间序列:
http_requests_total{job="apiserver", handler="/api/comments"}
返回同一向量的整个时间范围(在本例中为5分钟),使其成为一个范围向量:
http_requests_total{job="apiserver", handler="/api/comments"}[5m]
请注意,产生范围向量的表达式不能直接绘制图形,而是在表达式浏览器的表格(“Console”)视图中查看。
使用正则表达式,您可以仅为名称匹配特定模式的作业选择时间序列,在本例中,所有以server结尾的作业:
http_requests_total{job=~".*server"}
要选择除4xx以外的所有HTTP状态码,可以运行:
http_requests_total{status!~"4.."}
3.2 子查询(Subquery)
返回过去30分钟http_requests_total度量的5分钟速率,分辨率为1分钟。
rate(http_requests_total[5m])[30m:1m]
这是一个嵌套子查询的示例。派生函数的子查询使用默认分辨率。注意,不必要地使用子查询是不明智的。
max_over_time(deriv(rate(distance_covered_total[5s])[30s:5s])[10m:])
3.4 方法、操作符(Using functions, operators, etc.)
返回带有http_requests_total指标名称的所有时间序列的每秒速率,在过去5分钟内测量:
rate(http_requests_total[5m])
假设http_requests_total时间序列都有标签job(根据作业名称fanout)和instance(根据作业实例fanout),我们可能想要对所有实例的速率求和,这样我们得到更少的输出时间序列,但仍然保留作业维度:
sum by (job) ( rate(http_requests_total[5m]) )
如果我们有两个具有相同维度标签的不同度量,我们可以对它们应用二元运算符,两边具有相同标签集的元素将被匹配并传播到输出中。例如,该表达式返回MiB中每个实例的未使用内存(在一个虚构的集群调度程序上,暴露其运行的实例的这些指标):
(instance_memory_limit_bytes - instance_memory_usage_bytes) / 1024 / 1024
同样的表达式,但是通过应用求和,可以写成这样:
sum by (app, proc) ( instance_memory_limit_bytes - instance_memory_usage_bytes ) / 1024 / 1024
如果同一个虚构的集群调度程序暴露了每个实例的CPU使用率指标,如下所示:
instance_cpu_time_ns{app="lion", proc="web", rev="34d0f99", env="prod", job="cluster-manager"} instance_cpu_time_ns{app="elephant", proc="worker", rev="34d0f99", env="prod", job="cluster-manager"} instance_cpu_time_ns{app="turtle", proc="api", rev="4d3a513", env="prod", job="cluster-manager"} instance_cpu_time_ns{app="fox", proc="widget", rev="4d3a513", env="prod", job="cluster-manager"} ...
我们可以得到按应用程序(app)和进程类型(proc)分组的前3名CPU用户,如下所示:
topk(3, sum by (app, proc) (rate(instance_cpu_time_ns[5m])))
假设这个指标包含每个运行实例一个时间序列,你可以这样计算每个应用程序运行实例的数量:
count by (app) (instance_cpu_time_ns)
04 HTTP接口获取
当前稳定的HTTP API在Prometheus服务器的/ API /v1下是可访问的,详情参考:https://prometheus.io/docs/prometheus/latest/querying/api/
05 小结
本文主要讲解了PromSQL
的构造以及每块组成部分的解析,具体还需要去详细查看官方的文档,本文就不再过细的描述了。谢谢大家的阅读,本文完!