引言
在微服务架构和云原生应用中,监控和可观测性变得至关重要。Prometheus作为一款云原生领域开源监控系统,被广泛用于收集、存储及查询时间序列(Time Series)数据。PromQL(Prometheus Query Language)是Prometheus提供的时间序列数据查询语言,用于选择、聚合和计算时间序列数据,允许用户以灵活、强大的方式查询、分析监控数据。本文将深入探讨PromQL的主要功能、用法、示例以及常见应用场景,帮助读者更好地理解和使用PromQL。
PromQL概述
PromQL(Prometheus Query Language)是一种功能强大的查询语言,专为Prometheus设计。它允许用户以灵活的方式从时间序列数据库中选择、提取、聚合和分析数据,支持多种聚合和计算操作。PromQL的设计目标是提供一种简单且灵活的方式来查询、分析监控数据,使用户能够快速获取所需的信息。Prometheus提供两种查询:瞬时查询(instant query,查询某个时间点的数据)、范围查询( range query,在开始和结束时间之间以均匀间隔进行数据查询),可以将范围查询看做在不同时间点上多次进行瞬时查询。
PromQL基本概念
- 时间序列(Time Series):Prometheus中的基本数据单位,表示在特定时间点的度量值。
- 指标(Metric):时间序列名称,通常表示某种度量。例如,CPU使用率、内存使用量等。
- 标签(Label):时间序列元数据,用于标识和分类数据。例如,使用标签来区分不同服务、实例或机房。
数据类型
在Prometheus的表达式语言中,表达式或子表达式表现为如下四种类型:
- 瞬时向量(Instant vector):包含单个数据点的时间序列集合,且所有数据点具有相同的时间戳。
- 范围向量(Range vector):包含随时间变化的一系列数据点的时间序列集合。
- 标量(Scalar ):浮点数值。
- 字符串(String):字符串值。
PromQL语法
PromQL的语法相对简单,主要由以下几个部分组成:
1. 选择器(Selector)
选择器用于指示PromQL要获取哪些时间序列数据。选择器的基本格式如下:
metric_name{label_name="value"}
例如,选择指标名为http_requests_total的指标,并且标签method的值为GET,可以使用如下查询选择器:
http_requests_total{method="GET"}
瞬时向量选择器
瞬时向量选择器选择一组时间序列及其在给定时间戳(时间点)下的单个数据点。在最简单的形式中,通过指定一个指标名称,将产生包含所有具有该指标名称的时间序列的瞬时向量。例如选择所有具有http_requests_total指标名称的时间序列:
http_requests_total
可以在指标名称后面追加大括号({}),在其中添加以逗号(,)分隔的标签匹配器列表来进一步过滤这些时间序列。
下面的示例是仅选择那些具有http_requests_total指标名称且job标签设置为prometheus,并且group标签设置为canary的时间序列:
http_requests_total{job="prometheus",group="canary"}
可以对标签值进行负匹配,或将标签值与正则表达式进行匹配。有如下标签匹配运算符:
- =: 与提供字符串完全相等的标签。
- !=: 与提供的字符串不相等的标签。
- =~: 与提供的字符串正则匹配的标签。
- !~: 与提供的字符串正则不匹配的标签。
- 其中正则匹配是需要完全锚定的,如env =~"foo" 匹配为 env=~"^foo$"。
例如,选择环境(environment)标签为预上线(staging)、测试(testing)和开发(development)的http_requests_total指标,且HTTP方法标签不是GET的时间序列:
http_requests_total{environment=~"staging|testing|development",method!="GET"}
如果标签匹配的是空值则该匹配器会选择所有没有设置该标签的时间序列,假如有如下时间序列数据集:
http_requests_total
http_requests_total{replica="rep-a"}
http_requests_total{replica="rep-b"}
http_requests_total{environment="development"}
查询http_requests_total{environment=""}将匹配如下时间序列:
http_requests_total
http_requests_total{replica="rep-a"}
http_requests_total{replica="rep-b"}
会将如下environment不为空的时间序列排除:
http_requests_total{environment="development"}
对同一标签可以使用多个匹配器,而且所有匹配器都必须满足匹配条件才返回。如下查询:
http_requests_total{replica!="rep-a",replica=~"rep.*"}
将匹配如下时间序列:
http_requests_total{replica="rep-b"}
选择器必须指定指标名称或至少一个不匹配空字符串的标签匹配器。下面的表达式是非法的:
{job=~".*"} # 错误
下面这些表达式是有效的,因为它们都有一个不匹配空标签值的选择器:
{job=~".+"} # 正确!
{job=~".*",method="get"} # 正确!
标签匹配器可以通过匹配内部__name__标签应用于指标名称。例如,表达式 http_requests_total
等价于{__name__="http_requests_total"}
。除了 = 之外,还可以使用其他匹配运算符(!=、=~、!~)。下面的表达式选择所有名称以 job: 开头的指标:
{__name__=~"job:.*"}
指标名称不能是关键字 bool、on、ignoring、group_left 和 group_right 之一。以下表达式是非法的:
on{} # 错误
可以使用__name__标签来解决这个限制:
{__name__="on"} # 正确
注:Prometheus中使用的正则表达式都遵循 RE2 语法。
范围向量选择器
范围向量选择器的工作方式跟瞬时向量选择器相似,不同之处在于它会选择从当前时间戳向后查询一系列数据点。语法上,在选择器的末尾附加一个方括号([])来表示持续时间,这个时间值用来指定范围向量应获取从当前时间戳向前多远的元素。范围向量中的范围是一个闭区间,即与范围的任一边界重合的时间戳的样本都包含在该范围向量中。
下面示例中,选择具有http_requests_total
指标名称且job
标签为prometheus
过去5
分钟内所有数据点:
http_requests_total{job="prometheus"}[5m]
持续时间由数字和单位构成,可用单位如下:
- ms - 毫秒
- s - 秒
- m - 分钟
- h - 小时
- d - 天 - 假设一天始终有 24 小时
- w - 周 - 假设一周始终有 7 天
- y - 年 - 假设一年始终有 365 天
持续时间支持连接组合,单位必须按从长到短的顺序排列,且同一个单位在持续时间中只能出现一次。下面是有效的持续时间示例:
5h
1h30m
5m
10s
2.54版本开始,持续时间可以使用浮点数来表示持续时间的秒数。例如:
1.0 # 等价于 1s
0.001 # 等价于 1ms
120 # 等价于 2m
偏移(offset)修饰符
偏移修饰符用于在查询中更改瞬时和范围查询的时间偏移。
例如,查询 http_requests_total 在当前时间戳前 5 分钟的值:
http_requests_total offset 5m
注意,偏移修饰符需要紧跟选择器之后,下面是正确的格式:
sum(http_requests_total{method="GET"} offset 5m) // 正确
下面这个是错误的格式:
sum(http_requests_total{method="GET"}) offset 5m // 无效
offset 同样适用于范围查询。http_requests_total 在一周前的 5 分钟速率:
rate(http_requests_total[5m] offset 1w)
@ 修饰符
@ 修饰符用于在查询中更改瞬时和范围查询的当前时间戳。提供给 @ 修饰符的时间是一个 Unix 时间戳,并用浮点数值描述。
例如,http_requests_total 在 2021-01-04T07:40:00+00:00 的值:
http_requests_total @ 1609746000
注意,@ 修饰符需要紧跟选择器之后,下面是正确的格式:
sum(http_requests_total{method="GET"} @ 1609746000) // 正确
下面这个是错误的格式:
sum(http_requests_total{method="GET"}) @ 1609746000 // 无效
@ 修饰符同样适用范围查询。http_requests_total 在 2021-01-04T07:40:00+00:00 的 5 分钟速率:
rate(http_requests_total[5m] @ 1609746000)
@ 修饰符可以与偏移修饰符一起工作,这时偏移就应用于 @ 修饰符指定时间。这两个修饰符之间的顺序不会对结果产生影响。
例如,如下两个查询将产生相同的结果:
# 在 @ 之后偏移
http_requests_total @ 1609746000 offset 5m
# 在 @ 之前偏移
http_requests_total offset 5m @ 1609746000
此外,start() 和 end() 函数可以作为 @ 修饰符的值来使用。
对于范围查询来说,它们分别解析为范围查询的开始时间和结束时间。
对于瞬时查询,start() 和 end() 都解析为当前查询时间。
http_requests_total @ start()
rate(http_requests_total[5m] @ end())
2. 聚合函数(Aggregate Function)
PromQL提供了多种聚合函数,用于对时间序列数据进行汇总。常用聚合函数包括:
- sum(): 计算总和
- avg(): 计算平均值
- max(): 计算最大值
- min(): 计算最小值
- count(): 计算数量
例如,计算所有实例的http_requests_total总和,可以使用如下查询语句:
sum(http_requests_total)
3. 运算符(Operator)
PromQL支持多种运算符,包括加法、减法、乘法和除法。运算符可用于时间序列数据之间的计算。例如,计算CPU使用率与内存使用量的比率,可以使用如下查询语句:
sum(rate(cpu_usage_seconds_total[5m])) / sum(memory_usage_bytes)
4. 时间函数(Time Function)
PromQL提供了一些时间函数用于处理时间序列数据。常用的时间函数包括:
- rate(): 计算时间序列数据的单位时间变化率
- increase(): 计算时间序列数据的增量
- irate(): 计算瞬时变化率
例如,计算过去5分钟内每秒的请求速率,可以使用如下查询语句:
rate(http_requests_total[5m])
PromQL查询示例
基本查询
查询所有实例的http_requests_total指标:
http_requests_total
聚合查询
计算所有实例的http_requests_total总和:
sum(http_requests_total)
时间函数查询
计算过去10分钟内每秒的请求速率:
rate(http_requests_total[10m])
复杂查询
计算每个实例CPU使用率,并按实例(instance)进行分组:
sum(rate(cpu_usage_seconds_total[5m])) by (instance)
常见应用场景
- 监控CPU、内存使用情况
使用PromQL可以轻松监控CPU、内存的使用情况。例如,查询每个实例过去5分钟的CPU使用率:
sum(rate(cpu_usage_seconds_total[5m])) by (instance)
- 监控网络流量
PromQL还可以用于监控网络流量。例如,查询每个实例过去5分钟的网络接收、发送字节数:
sum(rate(network_receive_bytes_total[5m])) by (instance)
sum(rate(network_transmit_bytes_total[5m])) by (instance)
- 监控应用程序性能
通过PromQL可以监控应用程序的性能指标,如请求延迟、错误率等。例如,查询每个实例过去5分钟的请求延迟的95分位值:
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (instance))
- 监控自定义指标
PromQL允许监控自定义指标。例如,监控特定业务逻辑的指标,如过去1小时用户注册数增量:
sum(increase(user_registration_total[1h]))
性能优化
在使用PromQL时,性能是一个重要的考虑因素。下面是一些常用性能优化技巧:
- 合适的时间查询范围:查询时选择合适的时间范围,以避免不必要的数据查询处理。
- 避免过于复杂的查询:尽量简化查询,避免使用过多的聚合计算和运算符操作。
- 指标数据缓存:对于频繁查询的指标,可以考虑使用缓存机制。
如果需要查询处理大量数据,页面绘图可能会超时或使服务器、浏览器过载。因此,在构建未知规模的数据查询时,先从Prometheus的表格视图开始构建,直到结果看起来合理(最多数百个时间序列,而不是数千个时间序列)。只有在充分过滤或聚合后,才能切换到图形视图。如果仍然需要太长时间才能绘制图形,建议使用记录规则进行预先处理。此外,聚合多个时间序列即使输出只有少量时间序列结果,也会对服务器产生严重负载,这类似于在关系数据库中对一列的所有值求和,即使输出值只有一个数字,也会很慢。
结论
PromQL是一个简单、灵活且强大的查询语言,能够帮助用户从Prometheus中提取和分析时间序列监控数据。通过掌握PromQL的基本概念、语法和功能,可以更加有效地监控和优化应用程序、基础设施。希望本文能帮助您在实际工作中更好地使用PromQL。