博客/工程

Prometheus查询语言PromQL简介

2020年2月4日12分钟

PromQL是查询语言的一部分普罗米修斯.除了PromQL,普罗米修斯提供了从实例(任何提供指标的应用程序)和时间序列数据库(TSDB)中获取指标的scraper,时间序列数据库随时间存储这些指标。

对PromQL的介绍将在很大程度上与Prometheus的特定工具和非PromQL部分分离,以便将重点放在语言本身的特性上。

我推荐你阅读伊万娜·哈克娃的博客文章,如何探索普罗米修斯与简单的“你好世界”项目,其中有一些有用的提示和链接,可以帮助您使用Grafana建立系列文章和数据库。我们最近还介绍了普罗米修斯查询生成器Grafana 9,这使得构建简单和复杂的Prometheus查询变得容易——不需要PromQL经验。

你也可以在几分钟内开始使用普罗米修斯Grafana云.我们有新的免费和付费Grafana云计划,以适应每一个用例-现在免费注册

作为本文的补充,请查看PromCon EU 2019的Ian Billett的精彩演讲录音:PromQL for Mere Mortals.如果你对PromQL小抄感兴趣,可以看看Timber的人类的PromQL或PromLabs”PromQL备忘单

数据类型

Prometheus使用三种数据类型作为度量:标量、瞬时向量和范围向量。普罗米修斯最基本的数据类型是标量——它表示一个浮点值。标量的例子包括0、18.12和1000000。普罗米修斯中的所有计算都是浮点运算。

当您在单个时间点将标量作为一组指标组合在一起时,您将得到即时向量数据类型。当您运行只要求一个指标的名称的查询时,例如bicycle_distance_meters_total时,响应为瞬时矢量。因为指标有名称和标签(我将在后面稍微介绍),一个名称可能包含许多值,这就是为什么它是一个向量而不仅仅是一个标量。

一个随时间变化的向量数组就得到了范围向量。Grafana和内置的Prometheus表达式浏览器都没有直接使图形超出范围向量,而是使用即时向量或针对不同时间点独立计算的标量。

因此,范围向量通常被一个函数包装,该函数将其转换为要绘制的即时向量(如速率、增量或增量)。从语法上讲,当您查询一个即时向量并附加一个时间选择器(如[5m])时,您将得到一个范围向量。最简单的范围向量查询是带有时间选择器的即时向量,例如bicycle_distance_meters_total [1 h]

标签

一些早期的公制系统只有公制名称来区分不同的公制。这意味着您可能最终会使用度量名称,例如bicycle.distance.meters.monark.7以区分7齿轮的Monark自行车和2齿轮的Brompton自行车(bicycle.distance.meters.brompton.2).在《普罗米修斯》中,我们用标签来描述。标号写在指标名称之后,格式如下{标签=值}

这意味着我们之前的两个自行车被重写为bicycle_distance_meters_total{品牌= " monark "、齿轮=“7”}而且bicycle_distance_meters_total{品牌= "主管布朗普顿”地铁站,齿轮=“2”}

当基于指标进行查询时,使用相同的格式。因此,bicycle_distance_meters_total会给出这个例子中所有自行车的里程;bicycle_distance_meters_total{齿轮= " 7 "}会将结果限制为所有7档自行车。这让我们有了更大的灵活性,而不必像旧格式那样求助于奇怪的正则表达式魔法。

替换支持否定和正则表达式(在谷歌的re2格式中)! =! ~,或= ~分别表示不相等、不匹配和匹配。(当在Grafana中为变量选择多个值时,它们以兼容的格式表示= ~).

标签的缺点是看似无害的查询bicycle_distance_meters_total实际上可以返回数千个值,有时并不是很直观,哪些查询将在Prometheus服务器上结束,哪些查询将在查询Prometheus的客户端上结束。

指标类型

普罗米修斯概念上有四种不同的度量类型。度量类型都由一个或多个标量值表示,并使用一些不同的约定来说明如何使用它们以及它们的用途。

计数器而且是基本度量类型,两者都存储标量。计数器总是向上计数(重新启动时可以重置为零),而量规可以向上或向下计数。bicycle_distance_meters_total是一个计数器,因为自行车行驶的公里数不能减少,而bicycle_speed_meters_per_second应该是用来衡量速度的。按照惯例,计数器以_total帮助用户一目了然地区分计数器和仪表。

第三种数据类型是柱状图,它提供了一个单一的界面来测量三个不同的东西:

  1. <标准> _count是存储数据点总数的计数器。
  2. <标准> _sum是一个指示器,用于存储所有数据点相加的值。当不可能有负值时,总和可以用作所有直方图的计数器。
  3. <标准> _bucket是计数器的集合,其中使用标签支持计算值的分布。桶是累积的,因此在插入一个样本时,适用于某个值的所有桶都加1。有一个+正桶,它的值应该与_count

对于一场自行车比赛,按他们完成比赛所需的小时数完成比赛的自行车运动员的数量可以存储在一个直方图中,桶为21600、25200、28800、32400、36000、39600、+Inf。(时间按惯例以秒为单位存储;这是[6,11]小时范围内的每小时一桶。)

如果有2名自行车手在略少于7小时内完成,5人在少于8小时内完成,3人在少于10小时内完成,并且只有一名自行车手在两天后完成,桶将被表示成这样。(为了本例的目的,我已经创建了总和的值,但当然它可以是任何值,这取决于每个bucket中的值。)

race_duration_seconds_bucket{le="21600"} 0 race_duration_seconds_bucket{le="25200"} 2 race_duration_seconds_bucket{le="28800"} 7 race_duration_seconds_bucket{le="32400"} 7 race_duration_seconds_bucket{le="36000"} 10 race_duration_seconds_bucket{le="39600"} 10 race_duration_seconds_bucket{le="+Inf"} 11 race_duration_seconds_count 11 race_duration_seconds_sum 511200

通过对如何存储直方图有一个共同的约定,Prometheus可以提供诸如histogram_quantile(它为直方图计算分位数——我将在后面详细介绍),Grafana等外部工具可以识别格式并提供直方图功能。由于直方图“只是”计数器的集合,因此直方图不会增加的复杂性普罗米修斯在逃。

在使用直方图时,了解bucket的工作方式以及最大bucket之上的所有内容都被简单地存储为“大于最大bucket”,可以帮助您了解可以从直方图中获得什么样的精度,并进一步了解可以从计算中获得什么样的精度。

例如,有一个+正如果桶的值明显高于最大桶,则可能表明您的桶配置错误(并且您从Prometheus获得的值不可靠)。

度量的最后一种类型是总结.它类似于直方图,但被定义为分位数测量,由客户端计算以获得更高的分位数精度。预计算的摘要分位数不能以有意义的方式聚合。您可以从服务的各个实例中研究分位数,但不能将它们聚合到整个舰队范围内的分位数。

分位数的一个常见用例是作为服务水平指示器(即SLI/SLO/SLA),以了解发送到服务器的传入请求中有多少慢于50ms。在一个柱状图中,其中一个桶<0.05秒,可以非常准确地说明有多少请求在这段时间内没有被处理。添加更多的存储桶将使计算分位数成为可能,这将使您对性能有一个概念。对于摘要,这种聚合是完全不可能的。

总而言之,直方图要求您对值的分布有一定程度的了解,以便开始设置适当的存储桶,而摘要缺乏可靠的聚合操作。

函数和操作符

度量本身是有用的,但是为了最大化它们的效用,某种操作是必要的,这就是为什么Prometheus提供了许多操作符和函数。

聚合运算符

聚合运算符将一个即时向量缩减为表示相同或更少标签集的另一个即时向量,方法是聚合不同标签集的值,或者根据标签集的值保留一个或多个不同的集合。最简单的聚合运算符形式为avg (bicycle_speed_meters_per_second),它给出了集合中自行车的总体平均速度。

如果你想通过标签、品牌和齿轮数量来区分自行车,你可以使用Avg (bicycle_speed_meters_per_second) by(品牌,齿轮).”可以用如果您想放弃新向量的标签,而不是选择想要保留的标签,则不使用'。

有许多可用的聚合,最突出的是希望自解释的聚合总和最小值马克斯,avg.一些更复杂的聚合器需要额外的参数,例如bicycle_speed_meters_per_second topk (3),这样你就有了三个最高的速度。

二元操作符

算术二进制运算符(+,-,*,/,%[模/时钟计数],^[幂])可以操作即时向量和标量的组合,如果您试图在数学上合理,这可能会导致一些痛苦。我会总结不同的情况以及如何处理矢量运算中出现的奇怪情况。

标量到标量的算术是小学数学的核心。从标量到向量的算术几乎同样简单:对于向量中的每个值,应用标量的计算。(如果你有bicycle_speed_meters_per_second想要用千米每小时(km/h)来表示,这就完成了bicycle_speed_meters_per_second * 3.6.)

向量对向量的运算是它真正有趣的地方。这是标签匹配的过程,那些标签完全匹配的向量会被计算到彼此的方向。所有其他值将被丢弃。例子:bicycle_speed_meters_per_second/bicycle_cadence_revolutions_per_minute

因为这通常不是你想要的,你可以添加一个(或忽略)操作符的右边,并且在运行它之前,您将最终限制用于比较的标签集。但是,所有没有用于比较的标签都将被丢弃。这将是Bicycle_speed_meters_per_second / on (gears) bicycle_cadence_revolutions_per_minute

如果你想保留这些标签呢?你可以保留左边所有的标签,通过加group_left忽略关键字。当您这样做时,右边的值将应用到左边的每个匹配的标签上.实际上,这看起来像Bicycle_speed_meters_per_second / on (gears) group_left bicycle_cadence_revolutions_per_minute.还有一个group_right,而是按右侧分组。

除了算术运算符,还有比较(==,!=,>,<,>=,<=)和设置(and, or, unless)操作。

为即时向量和标量定义了比较操作。两个标量之间的比较返回0 (false)或1 (true),并且需要保龄球比较器后的关键字。对于瞬时向量,当与标量比较时,每个比较为真的数据点都被保留,而其他数据点则被丢弃。

当比较两个瞬时向量时,逻辑是相似的,但是对于每一组标签,标签和值都要进行比较。当操作返回false,或者在对面没有匹配标签集的度量时,该值将被丢弃;否则就会被保留。如果想要0或1而不是保留或丢弃该值,可以添加关键字保龄球在比较器之后。

集合操作符操作即时向量,并通过检查指标上的标签集来工作。为而且,如果一个标签集在左侧和右侧都存在,则返回左侧的值;否则,什么都没有。为,返回左边的所有标签集,以及左边不存在的右边的标签集。最后,除非返回左边标签集右边不存在的值。

功能

Prometheus中的函数的工作原理与编程中的函数非常相似,但仅限于预定义的集合。重要的是要知道大多数Prometheus的函数都是近似值和外推的结果——这偶尔会把应该是整数的计算变成浮点值,这也意味着当需要精确性时(例如,用于计费目的),Prometheus非常不适合使用。

一些特别有用的函数δ增加,.每个都接受一个范围向量作为输入,并返回一个即时向量。δ对量规进行操作,并返回量程的开始和结束之间的差值。增加而且操作计数器并返回计数器在指定时间内增加的数量。增加给出了在一段时间内的总增量,而速率给出了每秒钟的增量。率(bicycle_distance_meters_total h [1])应该和Increase (bicycle_distance_meters_total[1h]) / 3600.因为增加而且有逻辑处理重启时,值重置为零,重要的是要避免使用他们与仪表上升和下降。这最终可能看起来像是函数的重新启动,导致一个无意义的值。

为了理解柱状图桶,histogram_quantile函数有两个参数:第一个参数是应该计算的分位数,第二个参数是桶的瞬时向量。对于我们前面的比赛示例,添加了更多的标签,这可能是直方图(0.95,sum(race_duration_seconds_bucket) by (le)),它返回95%的选手完成比赛的时间。我们把完成时间和的原因(le)在执行分位数计算之前,是因为分位数是按标签的唯一组合计算的。这让我们能够绘制出诸如自行车上每个齿轮数的中位数时间等图形Histogram_quantile (0.5, sum(race_duration_seconds_bucket) by (le, gears))

阅读更多关于PromQL的信息

PromQL是一种特定于领域的语言,其语法隐藏了一些令人惊讶的地方,并且并不总是直观地可以理解。我最初在加入Grafana Labs之前写了这篇文章的一个早期版本,因为我想更好地理解Prometheus使用的语法,而且我注意到许多bob电竞频道PromQL查询在野外与它们的作者的意图不匹配。这篇文章是我的实验和阅读Prometheus的文档和源代码的总结。我建议阅读文档中的这些页面,它们涵盖了我在这里写的大部分内容,还有更多: