监控-Prometheus05-PromQL
- Prometheus提供了一种功能强大的表达式语言PromQL(Prometheus Query Language)。
- PromQL是Prometheus自己开发的数据查询DSL语言,允许用户实时选择和汇聚时间序列数据,能够进行计算和分析指标,使管理员能够更好地了解系统性能。
1、时序数据库
- 时序数据库(Time Series Database,TSDB)用于保存时间序列(按时间顺序变化)数据,是一种高性能、低成本、稳定可靠的专业化数据库。
- 它可以提供高效读写、高压缩比、低成本存储、降精度、插值、多维聚合计算和查询功能,解决由于设备采集点数据量巨大、数据采集频率高而造成的存储成本高、写入和查询分析效率低的问题。
- 时序数据库广泛应用于物联网监控系统、企业能源管理系统、生产安全监控、电力检测系统等行业场景。
- Google的监控系统经过10年的发展,经历了从传统的探针模型、图形化趋势展示模型到现在的基于时间序列数据信息进行监控报警的新模型。新模型将收集时间序列信息作为监控系统的首要任务,同时发展了一种时间序列信息操作语言,通过该语言将数据转化为图标和告警,从而取代了以前的探针脚本。
- 时序数据库的发展史:
- 关系数据库是可以存储时序数据的,但是由于其缺乏针对时间的特殊优化,例如按时间间隔存储和检索数据,因此在处理数据时效率相对低。
- 接着出现了源于监控领域的第一代时序数据存储系统,基于文件的简单存储工具成为这类数据的首选存储方式,以RRDTool、Wishper为代表,通常这类系统处理的数据模型比较单一,单机容量受限,并且内嵌于监控告警方案。
- 随着互联网大数据技术的出现和快速发展,时序数据量也开始迅速增长,系统业务对处理时序数据的扩展性等方面提出更多的要求。基于存储的时间序列数据库开始出现,它可以按时间间隔高效地存储和处理这些数据。以OpenTSDB、KairosDB为代表,这类时序数据库在继承存储优势的基础上,利用时序的特性规避部分通用存储的劣势,并且在数据模型以及聚合分析方面做了贴合时序的大量创新。
- 随着虚拟化技术的发展以及Docker、Kubernetes、微服务等技术的应用,高性能、低成本的垂直型时序数据库诞生了,以InfluxDB为代表,具有时序特征的数据存储引擎逐步引领市场。它们通常具备更加高级的数据处理能力、高效的压缩算法和符合时序特征的存储引擎。近年来时序数据库高速发展,国内外云市场各大主流厂商已经从整个时序生态的不同角度切入,形成各具特色的解决方案布局,开始抢占市场流量。
- 时间序列数据的特性如下:
- 数据写入特点:
- 写入平稳、持续、高并发高吞吐。
- 写多读少,在写操作上时序数据能达到95%以上。
- 无更新实时写入最近生成的数据。
- 数据查询特点:
- 按时间范围读取一段时间的数据。
- 对最近生成的数据读取概率高,对历史数据查询概率低。
- 按照数据点的不同密集度实现多精度查询。
- 数据存储特点:
- 数据存储量比较大。
- 具有时效性,数据通常会有一个保存周期。
- 多精度数据存储。
- 数据写入特点:
- 对时序数据库的基本要求如下:
- 能够支撑高并发、高吞吐的写入。
- 交互级的聚合查询,能够达到低查询延迟。
- 依据场景设计可以支撑海量数据存储。
- 在线应用服务场景中,需要高可用架构支撑。
- 针对写入和存储量的要求,应用环境底层需要分布式架构支持。
- 应用场景如下:
- 物联网设备监控分析。在物联网场景下,TSDB有广泛的应用。如在大型工业园区生产环境下,如果要全面覆盖,达到智能公共管控和服务效果,所使用的物联网智能设备将时刻产生海量的设备状态数据和业务消息数据。在这样的场景下,设备量非常大,种类多,采集指标多,指标上报频率高,实时性要求高。此时通过TSDB时序洞察可以满足亿级设备指标的快速写入和压缩存储,获取设备实时数据,快速分析每一种设备的整体运行概况,将时序分析的结果图形化地呈现给管理人员,进行设备监控、业务分析预测和故障诊断,帮助企业管理者分析数据。
- 智慧城市建设。在智慧城市建设大环境下,大量的智能物联网设备(如智能摄像头、智能路灯、智能道路测速仪等)的数据需要采集分析,达到智能公共管控和服务效果。在这样的场景下,同样是设备量非常大,种类多,采集指标多,指标上报频率高,实时性要求高。通过TSDB快速写入和压缩存储,可以获取设备实时数据,快速分析每一种设备的整体运行概况,将时序分析的结果图形化呈现给城市管理人员。
- 系统运维和业务实时监控。互联基础运维系统管理员通过对大规模应用集群和机房设备的监控,实时关注设备运行状态、资源利用率和业务趋势,实现数据化运营和自动化开发运维。通过TSDB时序洞察可以实时存储CPU、内存、负载、调用量等指标,支撑每秒百万级的指标数据写入,业务层无须代码开发,通过TSDB的计算实时进行任意维度组合分析,基于分析结果进行自动化故障报警和数据预测,达到智能运维的效果。
2、PromQL简介
- PromQL虽然以QL结尾,但它不是类似SQL的语言,因为在时间序列上执行计算类型时,SQL语言相对缺乏表达能力。而PromQL语言表现力非常丰富,可以使用标签进行任意聚合,还可以使用标签将不同的指标连接在一起进行算术操作。内置了日期和数学等很多的函数可供使用。
- 下面我们对Prometheus查询语言PromQL的一些知识点做简要介绍。
2.1、数据模型与数据类型
- Prometheus发布的2.0版本是一个完全重写的新存储引擎,新版本变化比较大,不对旧版本做兼容升级。它使用的底层存储具备了完整的持久化方案。
- Prometheus与其他主流时序数据库一样,在数据模型的核心定义上,一条Prometheus数据会包含一个指标名称(metric name)、一个或多个标签(label)和指标值(metric value)。
- 指标名称和一组标签可以唯一标识一个时间序列(time series),也就是时间线。
- 在查询时,支持根据标签条件查找时间序列(time series),支持简单的条件也支持复杂的条件。
- Prometheus的一条数据:
- 在此数据中指标名称是“promhttp_metric_handler_requests_total”,标签有code、instance、job,值是8。
1 | promhttp_metric_handler_requests_total{code= "200" , instance= "localhost:9090" , job= "prometheus" } 8 |
- 根据时序数据的特点,可以通过一张简单的数据点分布图来体现时序数据库的垂直写水平读,如图5-2所示。
- 从图5-2中可以看出,横轴是时间,纵轴是时间线,区域内的点就是数据点。可以形象地表述为Prometheus每次接收数据,收到的是图中区域内纵向的一条线。因为在同一时刻,每个时间序列只会产生一个数据点,但同时会有多个时间序列,把这些数据点连在一起,就是一条竖线。这个特征很重要,影响数据写入和压缩的优化策略。
- 在Prometheus的表达式语言中,PromQL数据类型归类为以下四种:
- 即时向量(instant vector):是指同一时刻的一组时间序列,每个时间序列包含一个样本,所有样本共享相同的时间戳,即每个时序只有一个点。
- 区间向量(range vector):是指在任何一个时间范围内的一组时间序列,包含每个时间序列随时间变化的一系列数据点,这时每个时序有多个点。
- 标量(scalar):即纯量数据,一个简单的数字浮点值,只有一个数字,没有时序。
- 字符串(string):一个目前未被使用的简单字符串值。
2.2、时间序列选择器
- 时间序列选择器有两种:
- 即时向量选择器
- 区间向量选择器
1、即时向量选择器
- 即时向量选择器(Instant vector selectors)返回0个、1个或多个时间序列上的指定时间截的各自的一个样本,该样本也可称为即时样本。
- 每个时间序列都只有一个样本,样本包含标签值和时间戳。
- 即时向量选择器由两部分组成:
- 指标名称:用于限定特定指标下的时间序列,即负责过滤指标(可选的)。
- 匹配器(Matcher):或称为标签选择器,用于过滤时间序列上的标签。定义在{}之中(可选的)。
- 定义即时向量选择器至少给出指标名称或与空字符串不匹配的标签匹配模式中的一个。存在以下三种组合:
- 仅给定指标名称,或在标签名称上使用了空值的标签匹配模式:返回给定指标下的所有时间序列的即时样本。
- 例如,prometheus_http_requests_total和prometheus_http_requests_total{}的效果相同,都是返回prometheus_http_requests_total指标的各时间序列的即时样本。
- 仅给定标签匹配模式:返回所有符合给定的匹配器的所有时间序列的即时样本。
- 注意:这些时间序列可能会有着不同的指标名称。
- {job=~".*"}表达式,是不合法的。
- {job=~".+"}和{job=~".*", method="get"}表达式是合法的,因为它们都有一个与空标签值不匹配的选择器。
- 同时给定指标名称和标签匹配模式:返回给定的指标下的,且符合给定的标签过滤器的所有时间序列上的即时样本。
- 例如,prometheus_http_requests_total{code="200"}
- 仅给定指标名称,或在标签名称上使用了空值的标签匹配模式:返回给定指标下的所有时间序列的即时样本。
- 目前支持如下4种匹配操作符:
- =:相等模式,返回与指定字符串相等的标签。
- !=:反向模式,返回与指定字符串不相等的标签。
- =~:正则表达式匹配模式。
- !~:反向正则表达式匹配模式。
- 另外,通过匹配内部标签__name__,还可以将标签匹配模式应用于监控指标的标准名称。例如,表达式http_requests_total等效于{__name__="http_requests_total"}。
- 在最简单的形式中,只指定一个指标名称,这将生成一个包含所有时间序列元素的即时向量,这些元素都具有这个指标名称。
- 例如,通过在Prometheus Web UI中的Graph页面中输入用于target的健康检查的“UP”表达式,再点击“Execute”进行查询,在Console栏目中可以看到Prometheus抓取到的所有target的当前运行情况,如图5-3所示。
2、区间向量选择器
- 区间向量选择器(Range Vector Selectors)返回0个、1个或多个时间序列上的指定时间范围内的各自的一组样本。从语法上看,时间范围被添加到区间向量选择器末尾的方括号[]中,用来指定获取过去一段时间范围内的样本数据。
- 在日常操作中,只有在调试期间才需要查看区间向量的原始样本,而实际操作时基本上都是使用rate或avg_over_time等函数,将范围向量作为参数。
- 定义区间向量选择器,和即时向量选择器大致相同,唯一不同之处在于需要在表达式后面使用一个时间值(使用方括号[]括起来),表示获取指定时间范围内的所有指标。
- PromQL的区间向量选择器支持的时间单位有:s(秒)、m(分钟)、h(小时)、d(天)、w(周)、y(年)。
- 必须使用整数时间,可以将多个不同级别的时间单位组合起来,时间单位要由大到小。例如1h30m,不能使用1.5h。
- 区间向量选择器返回的是一定时间范围内的数据样本,虽然不同时间序列的数据抓取时间点相同,但它们的时间戳并不会严格对齐。
- 多个Target上的数据抓取需要分散在抓取时间点前后一定的时间范围内,以均衡Prometheus Server的负载。
- 因而,Prometheus在趋势上准确,但并非绝对精准。
- 例如,使用浏览器直接在PrometheusWeb UI中的Graph页面中输入process_cpu_seconds_total[1m],然后在Console选项卡中执行,可以看到,每个时间序列在过去一分钟内有4个样本,如图5-5所示。
3、偏移量修改器
- 默认情况下,即时向量选择器和区间向量选择器都以当前时间为基准时间点,而偏移量修改器(Offset modifier)能够修改该基准时间点。
- 偏移量修改器的使用方法是在选择器表达式之后使用“offset”关键字指定要偏移的时间。
1 2 3 4 5 6 7 8 | //即时向量表达式,选择当前最新的数据 process_resident_memory_bytes{job= "prometheus" } //区间向量表达式,选择以当前时间为基准的15分钟内的数据 process_resident_memory_bytes{job= "prometheus" } [15m] //获取1小时以前的即时向量和区间向量(使用offset关键字) process_resident_memory_bytes{ job= "prometheus" } offset 1h process_resident_memory_bytes{ job= "prometheus" } [15m] offset 1h |
- 实际使用偏移量修改器的方式
1 2 3 4 5 | //给出prometheus在过去一小时内内存使用量的变化 process_resident_memory_bytes{job= "prometheus" } - process_resident_memory_bytes{job= "prometheus" } offset 1h //同样的操作方法也适用于区间向量 rate(process_resident_memory_bytes{job= "prometheus" } [15m]) - rate(process_resident_memory_bytes{job= "prometheus" } [15m] offset 1h) |
3、PromQL聚合操作
- Prometheus为使用者提供了内置的聚合操作符,这些聚合操作符仅仅适用于对单个即时向量进行聚合操作。它们可以将即时向量聚合后生成一个包含较少元素的新的时间序列。
- 聚合操作的语法:
1 2 3 4 | //两者的效果相同 <aggr-op>([parameter,] <vector expression>) [without|by (<label list>)] <aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>) |
-
- 分组聚合:先分组、后聚合
- by关键字使用列出的标签进行分组。
- without关键字使用未列出的标签进行分组。即先删除列出的标签,在使用剩余的标签进行分组。
- aggr-op:聚合操作符。
- sum:求和。将组中的所有值相加,并将“和”作为组的值返回。
- min:最小值。将组中的最小值作为组的值返回。
- max:最大值。将组中的最大值作为组的值返回。
- avg:平均值。将组中的所有值的平均值作为组的值返回。
- stddev:标准差。计算组中的所有值的标准差,并作为组的值返回。
- stdvar:标准方差。计算组中的所有值的标准方差,并作为组的值返回。
- count:计数。统计组中时间序列的数量,并作为组的值返回。
- count_values:对相同value进行计数,用于统计时间序列中每一个样本值出现的次数。court_valaes会将每个唯一的样本值输出一个时间序列,并且每一个时间序列包含一个额外的标签。这个标签的名字由聚合参数指定,同时这个标签值是唯一的样本值。
- bottomk:对样本值进行排序,并返回后N个时间序列。
- topk:对样本值进行排序,并返回前N个时间序列。
- quantile:分位数,用于评估数据的分布状态,该函数会返回分组内指定的分位数的值,即数值落在小于等于指定的分位区间的比例。
- parameter:为可选参数,聚合的参数。
- 分组聚合:先分组、后聚合
- 例如,指标prometheus_http_requests_total由code、handler、instance和job的标签组成的时间序列数据,则可以通过sum操作符进行如下计算,统计不同请求响应码的数量:
1 2 3 | //两个表达式的效果是相同的 sum(prometheus_http_requests_total) by (code) sum(prometheus_http_requests_total) without (handler, instance, job) |
4、PromQL运算符
- 当用户需要使用不同的监控指标进行更多操作时,PromQL聚合操作会出现无法满足使用的情况。这时Prometheus提供了多种运算符。这些运算符不仅允许对即时向量进行简单的算术运算,还可以将运算符应用于两个基于标签分组的即时向量。
- Prometheus查询语言提供的运算符有:
- 算术运算符
- 关系运算符
- 向量匹配模式
- 逻辑运算符
4.1、算术运算符
- 算术运算符有6种:
- +(加)、-(减)、*(乘)、/(除) 、%(取模)和^(幂运算)
- 算术运算操作符支持三类操作:
- scalar/scalar(标量/标量)之间的操作
- 在两个标量之间进行算术运算,得到的结果还是标量。
- vector/scalar(即时向量/标量)之间的操作
- 即时向量与标量之间进行算术运算时,会使用算术运算符将标量依次作用于即时向量中的每一个样本值,从而得到一组新的时间序列(返回值不带指标名,如图5-7)。
- vector/vector(即时向量/即时向量)之间的操作
- 对两个即时向量进行运算的方法,见下面的向量匹配。
- scalar/scalar(标量/标量)之间的操作
4.2、关系运算符
- 关系运算符有6种:
- ==(相等)、!=(不相等)、>(大于)、<(小于)、>=(大于等于)和<=(小于等于)
- 默认情况下,关系运算符用于对时序数据进行过滤。但是在有些情况下,可以通过在关系运算符之后使用bool修饰符,从而不对时间序列进行过滤,而是直接返回0(false)或者1(true)。
- 关系运算操作符支持三类操作:
- scalar/scalar(标量/标量)之间的操作
- 在两个标量之间进行关系运算,必须使用bool修饰符(如图5-9),得到的结果还是标量,即0(false)或者1(true)。
- vector/scalar(即时向量/标量)之间的操作
- 即时向量与标量之间进行关系运算时,会使用关系运算符将标量依次作用于即时向量中的每一个样本值,如果样本值与这个标量的比较结果是false,则这个时间序列数据被丢弃,如果是true,则这个时间序列数据被保留在结果中。(如图5-10)
- vector/vector(即时向量/即时向量)之间的操作
- 即时向量与即时向量之间进行关系运算时,运算符默认情况下是过滤的,用于匹配条目。表达式不是true或在表达式的另一侧找不到匹配项的向量元素将被从结果中删除,即不在结果中显示;否则将保留左侧的度量指标和标签的样本数据写入即时向量。如果提供了bool修饰符,则删除的向量元素的值为0,而保留的向量元素的值为1,左侧标签值为1。
- scalar/scalar(标量/标量)之间的操作
4.3、向量匹配
- 在两个即时向量之间使用运算符时,哪些样本应该适用于哪些其他样本?这种即时向量的匹配称为向量匹配。
- Prometheus提供了两种基本的向量匹配模式:
- one-to-one向量匹配
- many-to-one(one-to-many)向量匹配
1、一对一向量匹配模式
- 一对一向量匹配模式(one-to-one):
- 从运算符的两边表达式中获取即时向量,依次比较并找到唯一匹配(标签完全一致)的一对样本值。
- 不能匹配的时间序列不会出现在结果中。
- 一般默认(具有标签完全一致的时间序列)的表达式格式是:
1 | vector1 <operator> vector2 |
- 如果运算符两侧表达式标签不一致,可以使用关键字on或ignoring修改标签间的匹配行为。表达格式分别为:
1 2 3 4 5 | //on:只使用指定的标签进行匹配 <vector expr> <bin-op> on(<label list>) <vector expr> //ignoring:忽略指定的标签进行匹配 <vector expr> <bin-op> ignoring(<label list>) <vector expr> |
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //数据样本 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 //(一对一向量匹配模式)先筛选出第一个指标中的code="500",然后在忽略标签code,最后进行一对一向量匹配 method_code:http_errors:rate5m{code= "500" } / ignoring(code) method:http_requests:rate5m //计算后的结果 {method= "get" } 0.04 // 24 / 600 (method_code:http_errors:rate5m{method="get", code="500"} / method:http_requests:rate5m{method="get"}) {method= "post" } 0.05 // 6 / 120 (method_code:http_errors:rate5m{method="post", code="500"} / method:http_requests:rate5m{method="post"}) |
2、多对一和一对多的匹配模式
- 多对一和一对多的匹配模式(many-to-one and one-to-many):
- “一”侧的每个元素,可与“多”侧的多个元素进行匹配。
- 必须使用group_left或group_right明确指定哪侧为“多”侧。
1 2 3 4 5 | <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> |
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //数据样本 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 //左向量对每个方法标签值包含多个条目。因此,使用group_left来表示这一点。 method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m //计算后的结果 {method= "get" , code= "500" } 0.04 // 24 / 600 (method_code:http_errors:rate5m{method="get", code="500"} / method:http_requests:rate5m{method="get"}) {method= "get" , code= "404" } 0.05 // 30 / 600 (method_code:http_errors:rate5m{method="get", code="404"} / method:http_requests:rate5m{method="get"}) {method= "post" , code= "500" } 0.05 // 6 / 120 (method_code:http_errors:rate5m{method="post", code="500"} / method:http_requests:rate5m{method="post"}) {method= "post" , code= "404" } 0.175 // 21 / 120 (method_code:http_errors:rate5m{method="post", code="404"} / method:http_requests:rate5m{method="post"}) |
4.4、逻辑运算符
- 逻辑运算符有三种:
- and、or和unless
- 逻辑运算符仅用于向量与向量之间。所有逻辑运算符都以多对多的方式工作,它们是唯一能工作于多对多方式的运算符。
- 不同于算术运算符和比较运算符,因为没有执行任何数学计算,所以重点是描述一个组是否包含样本。
1 2 3 4 5 6 7 8 | //and逻辑运算会产生一个由vector1的元素组成的新的向量。该向量包含vector1中完全匹配vector2中的元素。 vector1 and vector2 //or逻辑运算会产生一个新的向量,该向量包含vector1的所有原始元素(标签集+值)的向量,以及vector2中没有与vector1匹配标签集的所有元素。 vector1 or vector2 //unless逻辑运算会产生一个由vector1的元素组成的向量,而这些元素在vector2中没有与标签集完全匹配的元素,两个向量中的所有匹配元素都被删除。 vector1 unless vector2 |
4.5、运算符优先级
- Prometheus运算符的优先级,由高到低的:
- ^
- *、/、%
- +、-
- ==、!=、<、>、<=、>=
- and、unless
- or
5、PromQL函数
- Prometheus提供了多种功能的函数供用户使用,包括数学函数、时间函数、标签操作函数、Counter指标增长率计算函数等。
- 说明文档:https://prometheus.io/docs/prometheus/latest/querying/functions/#label_replace
5.1、数学函数
- 数学函数对即时向量执行标准的数学运算,如计算绝对值或取对数。即时向量中的每个样本都是独立处理的,并且返回值没有指标名。
1、abs()
- 该函数返回即时向量中值的绝对值。
示例:
1 2 3 4 5 6 7 8 9 10 11 | //process_open_fds表达式,结果返回 process_open_fds{env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 10 process_open_fds{instance= "localhost:9090" , job= "prometheus" } 37 //process_open_fds - 100表达式,结果返回 {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } -91 {instance= "localhost:9090" , job= "prometheus" } -64 //abs(process_open_fds - 100)表达式,结果返回 {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 91 {instance= "localhost:9090" , job= "prometheus" } 64 |
2、sqrt()
- 该函数返回即时向量中值的平方根。
示例:
1 2 3 4 5 6 7 | //process_open_fds表达式,结果返回 process_open_fds{env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 9 process_open_fds{instance= "localhost:9090" , job= "prometheus" } 36 //sqrt(process_open_fds)表达式,结果返回 {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 3 {instance= "localhost:9090" , job= "prometheus" } 6 |
3、round()
- 该函数将即时向量中的值四舍五入到最近的整数。
4、clamp_max()和clamp_min()
- clamp_max():该函数返回小于指定值的即时向量。
- clamp_minx():该函数返回大于指定值的即时向量。
1 2 3 4 5 6 7 8 9 10 11 | //process_open_fds表达式,结果返回 process_open_fds{env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 9 process_open_fds{instance= "localhost:9090" , job= "prometheus" } 36 //查找进程打开文件描述符数量小于78的表达式为clamp_min(process_open_fds, 33) {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 9 {instance= "localhost:9090" , job= "prometheus" } 33 //查找进程打开文件描述符数量大于33的表达式为clamp_min(process_open_fds, 33) {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 33 {instance= "localhost:9090" , job= "prometheus" } 36 |
5.2、时间函数
- Prometheus使用的是协调世界时(UTC),没有时区的概念。为了使用户在使用中不用自己实现与日期相关的逻辑,Prometheus提供了一些时间函数。
1、time()
- 返回自UTC 1970年1月1日以来的秒数。
示例:
1 2 3 | //查看进程运行了多长时间的表达式time() - process_start_time_seconds {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 263824.1900000572 {instance= "localhost:9090" , job= "prometheus" } 186246.50999999046 |
2、时钟和日历类函数
- year():返回给定时间的年份
- month():返回给定时间的月份。返回值为1~12
- day_of_month():返回给定时间是几号(一个月的第几天的值),返回值为1~31
- hour():返回给定时间的小时,返回值0~23
- minute():返回给定时间的分钟,返回值为0~59
- days_in_month():返回给定时间的月份的天数,返回值为28~31
- day_of_week():返回给定时间是星期几,返回值为0~6,其中0表示星期日
示例:
1 2 3 4 5 6 7 8 9 | //返回年份 year(process_start_time_seconds) {instance= "localhost:9090" , job= "prometheus" } 2022 //返回是几月 month(process_start_time_seconds) {instance= "localhost:9090" , job= "prometheus" } 10 //返回是几号 day_of_month(process_start_time_seconds) {instance= "localhost:9090" , job= "prometheus" } 14 |
5.3、标签操作函数
1、label_replace()
- 该函数的格式如下:
- 对于v中的每个时间序列,根据标签src_label的值匹配正则表达式regex。
- 如果匹配,则返回的时间序列中的标签dst_label的值替换成replacement(可以使用正则表达式的捕获组,用2等引用),以及输入中的原始标签。
- 如果正则表达式不匹配,则返回不变的时间序列。
- 对于v中的每个时间序列,根据标签src_label的值匹配正则表达式regex。
1 | label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) |
示例:
1 2 3 4 5 6 7 | //up表达式,结果返回 up{env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 1 up{instance= "localhost:9090" , job= "prometheus" } 1 //label_replace(up, "host", "$1", "instance", "(.*):.*")表达式,结果返回 up{env= "dev_node_exporter" , host= "10.1.1.13" , instance= "10.1.1.13:9100" , job= "node" } 1 up{host= "localhost" , instance= "localhost:9090" , job= "prometheus" } 1 |
2、label_join()
- 该函数的格式如下:
- 对于v中的每个时间序列,使用分隔符(separator)连接所有src_label的所有值,并返回带有标签dst_label的时间序列,其中包含连接的值。这个函数中可以有任意数量的src_label。
1 | label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...) |
示例:
1 2 3 4 5 6 7 | //up表达式,结果返回 up{env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 1 up{instance= "localhost:9090" , job= "prometheus" } 1 //label_join(up, "host", "_", "instance", "job")表达式,结果返回 up{env= "dev_node_exporter" , host= "10.1.1.13:9100_node" , instance= "10.1.1.13:9100" , job= "node" } 1 up{host= "localhost:9090_prometheus" , instance= "localhost:9090" , job= "prometheus" } 1 |
5.4、Counter指标增长率
1、increase()
- 该函数的格式如下:
- 只能与计数器类型的指标一起使用,用于计算区间向量中时间序列的增量。它获取区间向量中的第一个和最后一个样本,并返回其增长量。
1 | increase(v range-vector) |
示例:
- 指标process_cpu_seconds_total(用户和系统CPU总时间,均以秒为单位)是counter类型,通过process_cpu_seconds_total[3m]获取时间序列最近3分钟的所有样本,increase计算出最近3分钟的增长量,最后除以时间180秒得到process_cpu_seconds_total样本在最近3分钟的平均增长率。
1 2 3 | //increase(process_cpu_seconds_total[3m]) / 180表达式,结果返回 {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 0.0056363636363640215 {instance= "localhost:9090" , job= "prometheus" } 0.0008484848484847658 |
2、rate()
- 该函数的格式如下:
- 只能与计数器类型的指标一起使用,用于计算区间向量中时间序列的每秒平均增长率。当因目标重启而导致计数器重置时,会自动调整。它是在实际工作中使用PromQL时的主要函数,最适用于告警和缓慢移动计数器的图形。
1 | rate(v range-vector) |
示例:
1 2 3 | //process_cpu_seconds_total样本在最近3分钟的平均增长率的表达式rate(process_cpu_seconds_total[3m]) {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 0.006000000000000055 {instance= "localhost:9090" , job= "prometheus" } 0.0007272727272727548 |
3、irate()
- 该函数的格式如下:
- 计算区间向量中时间序列的每秒即时增长率,是一个灵敏度更高的函数。当因目标重启而导致计数器重置时,会自动调整。
- irate函数是通过区间向量中最后两个数据点来计算区间向量的增长速率,反映的是即时增长率,绘制出的图形可以更好地反映样本数据的即时变化状态。
1 | irate(v range-vector) |
- 由于irate函数相比于rate函数灵敏度更高,在需要分析长期趋势或者告警规则的场景中,irate的这种灵敏度会容易形成干扰,所以在长期趋势分析或者告警中建议使用rate函数。
示例:
1 2 3 | //irate(process_cpu_seconds_total[3m])表达式,结果返回 {env= "dev_node_exporter" , instance= "10.1.1.13:9100" , job= "node" } 0.007330889703425521 {instance= "localhost:9090" , job= "prometheus" } 0.0006666666666670077 |
5.5、Gauge指标趋势变化预测
1、predict_linear()
- 该函数的格式如下:
- 基于区间向量,使用简单的线性回归预测时间序列 t 秒的值,从而可以对时间序列的变化趋势做出预测。
1 | predict_linear(v range-vector, t scalar) |
- 在传统监控环境中,管理员可以对资源使用最高值设定阈值,只要资源使用达到或超过该阈值,告警通知管理员进行人工问题处理。但是对于突发式增长类业务,或因不可预知的程序bug而造成主机或云资源使用暴涨时,传统监控告警机制中,管理员或对应业务人员收到告警后,可能已经无法进行问题处理了,此时系统已经是宕机状态了。这种场景刚好适合使用本节介绍的predict_linear函数,通过设定合理的时间范围,对资源消耗趋势做出预测。
示例:
- 根据过去1小时的样本数据,预测主机的可用磁盘空间是否在4小时内被占满。
1 | predict_linear(node_filesystem_free_bytes{mountpoint= "/" } [1h], 4 * 3600) < 0 |
1
1 | # # |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?