• 初识PromQL
    • 查询时间序列
    • 范围查询
    • 时间位移操作
    • 使用聚合操作
    • 标量和字符串
      • 标量(Scalar):一个浮点型的数字值
      • 字符串(String):一个简单的字符串值
    • 合法的PromQL表达式

    初识PromQL

    Prometheus通过指标名称(metrics name)以及对应的一组标签(labelset)唯一定义一条时间序列。指标名称反映了监控样本的基本标识,而label则在这个基本特征上为采集到的数据提供了多种特征维度。用户可以基于这些特征维度过滤,聚合,统计从而产生新的计算后的一条时间序列。

    PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。并且被广泛应用在Prometheus的日常应用当中,包括对数据查询、可视化、告警处理当中。可以这么说,PromQL是Prometheus所有应用场景的基础,理解和掌握PromQL是Prometheus入门的第一课。

    查询时间序列

    当Prometheus通过Exporter采集到相应的监控指标样本数据后,我们就可以通过PromQL对监控样本数据进行查询。

    当我们直接使用监控指标名称查询时,可以查询该指标下的所有时间序列。如:

    1. http_requests_total

    等同于:

    1. http_requests_total{}

    该表达式会返回指标名称为http_requests_total的所有时间序列:

    1. http_requests_total{code="200",handler="alerts",instance="localhost:9090",job="prometheus",method="get"}=(20889@1518096812.326)
    2. http_requests_total{code="200",handler="graph",instance="localhost:9090",job="prometheus",method="get"}=(21287@1518096812.326)

    PromQL还支持用户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式:完全匹配和正则匹配。

    PromQL支持使用=!=两种完全匹配模式:

    • 通过使用label=value可以选择那些标签满足表达式定义的时间序列;
    • 反之使用label!=value则可以根据标签匹配排除时间序列;

    例如,如果我们只需要查询所有http_requests_total时间序列中满足标签instance为localhost:9090的时间序列,则可以使用如下表达式:

    1. http_requests_total{instance="localhost:9090"}

    反之使用instance!="localhost:9090"则可以排除这些时间序列:

    1. http_requests_total{instance!="localhost:9090"}

    除了使用完全匹配的方式对时间序列进行过滤以外,PromQL还可以支持使用正则表达式作为匹配条件,多个表达式之间使用|进行分离:

    • 使用label=~regx表示选择那些标签符合正则表达式定义的时间序列;
    • 反之使用label!~regx进行排除;

    例如,如果想查询多个环节下的时间序列序列可以使用如下表达式:

    1. http_requests_total{environment=~"staging|testing|development",method!="GET"}

    范围查询

    直接通过类似于PromQL表达式httprequeststotal查询时间序列时,返回值中只会包含该时间序列中的最新的一个样本值,这样的返回结果我们称之为瞬时向量。而相应的这样的表达式称之为__瞬时向量表达式

    而如果我们想过去一段时间范围内的样本数据时,我们则需要使用区间向量表达式。区间向量表达式和瞬时向量表达式之间的差异在于在区间向量表达式中我们需要定义时间选择的范围,时间范围通过时间范围选择器[]进行定义。例如,通过以下表达式可以选择最近5分钟内的所有样本数据:

    1. http_request_total{}[5m]

    该表达式将会返回查询到的时间序列中最近5分钟的所有样本数据:

    1. http_requests_total{code="200",handler="alerts",instance="localhost:9090",job="prometheus",method="get"}=[
    2. 1@1518096812.326
    3. 1@1518096817.326
    4. 1@1518096822.326
    5. 1@1518096827.326
    6. 1@1518096832.326
    7. 1@1518096837.325
    8. ]
    9. http_requests_total{code="200",handler="graph",instance="localhost:9090",job="prometheus",method="get"}=[
    10. 4 @1518096812.326
    11. 4@1518096817.326
    12. 4@1518096822.326
    13. 4@1518096827.326
    14. 4@1518096832.326
    15. 4@1518096837.325
    16. ]

    通过区间向量表达式查询到的结果我们称为区间向量

    除了使用m表示分钟以外,PromQL的时间范围选择器支持其它时间单位:

    • s - 秒
    • m - 分钟
    • h - 小时
    • d - 天
    • w - 周
    • y - 年

    时间位移操作

    在瞬时向量表达式或者区间向量表达式中,都是以当前时间为基准:

    1. http_request_total{} # 瞬时向量表达式,选择当前最新的数据
    2. http_request_total{}[5m] # 区间向量表达式,选择以当前时间为基准,5分钟内的数据

    而如果我们想查询,5分钟前的瞬时样本数据,或昨天一天的区间内的样本数据呢? 这个时候我们就可以使用位移操作,位移操作的关键字为offset

    可以使用offset时间位移操作:

    1. http_request_total{} offset 5m
    2. http_request_total{}[1d] offset 1d

    使用聚合操作

    一般来说,如果描述样本特征的标签(label)在并非唯一的情况下,通过PromQL查询数据,会返回多条满足这些特征维度的时间序列。而PromQL提供的聚合操作可以用来对这些时间序列进行处理,形成一条新的时间序列:

    1. # 查询系统所有http请求的总量
    2. sum(http_request_total)
    3. # 按照mode计算主机CPU的平均使用时间
    4. avg(node_cpu) by (mode)
    5. # 按照主机查询各个主机的CPU使用率
    6. sum(sum(irate(node_cpu{mode!='idle'}[5m])) / sum(irate(node_cpu[5m]))) by (instance)

    标量和字符串

    除了使用瞬时向量表达式和区间向量表达式以外,PromQL还直接支持用户使用标量(Scalar)和字符串(String)。

    标量(Scalar):一个浮点型的数字值

    标量只有一个数字,没有时序。

    例如:

    1. 10

    需要注意的是,当使用表达式count(http_requests_total),返回的数据类型,依然是瞬时向量。用户可以通过内置函数scalar()将单个瞬时向量转换为标量。

    字符串(String):一个简单的字符串值

    直接使用字符串,作为PromQL表达式,则会直接返回字符串。

    1. "this is a string"
    2. 'these are unescaped: \n \\ \t'
    3. `these are not unescaped: \n ' " \t`

    合法的PromQL表达式

    所有的PromQL表达式都必须至少包含一个指标名称(例如http_request_total),或者一个不会匹配到空字符串的标签过滤器(例如{code=”200”})。

    因此以下两种方式,均为合法的表达式:

    1. http_request_total # 合法
    2. http_request_total{} # 合法
    3. {method="get"} # 合法

    而如下表达式,则不合法:

    1. {job=~".*"} # 不合法

    同时,除了使用<metric name>{label=value}的形式以外,我们还可以使用内置的__name__标签来指定监控指标名称:

    1. {__name__=~"http_request_total"} # 合法
    2. {__name__=~"node_disk_bytes_read|node_disk_bytes_written"} # 合法