MQL 语言简介

本页面提供 Monitoring Query Language (MQL) 的常规信息,包括以下主题:

无论您是通过 Google Cloud Console 还是通过 Cloud Monitoring API 使用 MQL,此信息都适用。如需了解 MQL 查询的结构,请参阅示例

表操作和函数的快捷方式

查询通常由竖线 (|) 连接的表操作链组成,每个表操作都以表操作的名称开头,后跟表达式列表。表达式可以包含显式列出其所有参数的函数调用。但是,Monitoring Query Language 允许通过多个快捷方式表示查询。

本部分介绍表操作的快捷方式,将函数用作表操作,以及值列作为函数参数时的快捷方式。

如需完整列表,请参阅表操作快捷方式

表操作的快捷方式

使用 fetchgroup_byfilter 操作时,如果参数足以确定预期的操作,则可以省略显式表操作。例如,以下查询:

gce_instance::compute.googleapis.com/instance/cpu/utilization

等效于:

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization

以下 group_by 操作是等效的:

         [zone], mean(val())
group_by [zone], mean(val())

如果您为过滤器测试添加了括号,则可以省略 filter 一词。例如,以下两个 filter 操作是等效的:

       (instance_name =~ 'apache.*')
filter instance_name =~ 'apache.*'

您可以在查询中组合使用这些快捷方式。例如,以下查询:

gce_instance::compute.googleapis.com/instance/cpu/utilization
| (instance_name =~ 'apache.*')
|  [zone], mean(val())

等效于以下更为明确的形式:

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization
| filter instance_name =~ 'apache.*'
| group_by [zone], mean(val())

如需详细了解表操作的快捷方式,请参阅 MQL 参考文档中的表操作快捷方式

将函数用作表操作

表操作通常以表操作的名称开头。但是,MQL 允许表操作以函数名称开头。

如果指定函数是要针对输入表的值列执行一些转换,则可以使用函数名称。此替换是 group_byalignvalue 表操作的快捷方式,具体取决于给定名称的函数的种类。

通用格式如下:

|  FUNCTION_NAME ARG, ARG ... 

在表操作中,函数将输入表的值列作为参数,后跟函数本身的所有参数。将函数用作表操作时,以表操作形式指定参数(逗号分隔列表),而不是使用函数常用的括号 (())。

通过展开快捷方式生成的完整表操作取决于函数的种类:

  • group_by:如果您将聚合函数与聚合所有时间序列标识符列的 group_by 操作(即,使用 [] 进行分组)搭配使用,则该函数可用作快捷方式。例如:

    | distribution powers_of(1.1)

    是以下表操作的快捷方式

    | group_by [], distribution(val(0), powers_of(1.1))
  • align:如果您将校准函数用作 align 操作的参数,则可以将函数作为快捷方式。例如:

    | delta

    是以下表操作的快捷方式

    | align delta()

    同样,

    | rate 10m

    是以下表操作的快捷方式

    | align rate(10m)

    请注意,校准器函数将输入时间序列作为隐式参数,因此此处没有显式提供值列。

  • value:其他所有函数可以充当 value 表操作的快捷方式。例如:

    | mul 3.3

    是以下表操作的快捷方式

    | value mul(val(0), 3.3)

    同样,

    | div

    是以下表操作的快捷方式

    | value div(val(0), val(1))

    请注意,div 快捷方式接受具有两个值列的输入表操作,并生成包含一个值列(即比率)的表操作。

值列函数的快捷方式

如果输入具有单个值列,则可以使用 .function 作为 function(val()) 的快捷方式;如果具有两个值列,则可用作 function(val(0), val(1)) 的快捷方式,以此类推。

前导句点表示“调用以下函数,将一个或多个输入点值列作为函数的参数提供。”

例如,.meanmean(val()) 的快捷方式。以下表达式是等效的:

group_by [zone], .mean
group_by [zone], mean(val())

如果输入表有多个值列,则每一列都会成为此快捷方式中函数的参数。例如,如果输入表有两个值列,则

.div

是以下表操作的快捷方式

div(val(0), val(1))

使用此快捷方式,您可以提供不引用值列的参数。其他参数在值列参数后提供。例如,如果输入表具有一个值列,则

.div(3)

等效于

div(val(0), 3)

fetch 的变体

fetch 操作通常返回由一对受监控的资源和指标类型命名的时间序列表。例如:

fetch gce_instance :: compute.googleapis.com/instance/cpu/utilization

如果指标仅适用于一个受监控的资源类型,则可以在查询中省略受监控的资源。以下查询与上述查询等效,因为 CPU 利用率指标仅适用于 gce_instance 受监控的资源:

fetch compute.googleapis.com/instance/cpu/utilization

fetch 操作只能指定一个受监控的资源类型,并在后续 metric 操作中指定指标。例如,以下示例与上述 fetch 示例等效:

fetch gce_instance
| metric metric compute.googleapis.com/instance/cpu/utilization

如果您想为同一个受监控的资源提取两个不同的指标,以这种方式拆分 fetch 会非常有用。例如,以下查询计算每 CPU 秒使用的数据包数量:

fetch gce_instance
| {
    metric compute.googleapis.com/instance/network/received_packets_count ;
    metric compute.googleapis.com/instance/cpu/usage_time
  }
| ratio

此外,拆分 fetch 还允许您仅将过滤应用于受监控的资源的标签:

fetch gce_instance
| filter resource.zone =~ "asia.*"
| {
    metric compute.googleapis.com/instance/network/received_packets_count ;
    metric compute.googleapis.com/instance/cpu/usage_time
  }
| ratio

仅命名一个受监控的资源类型的 fetch 必须后跟 metric 操作,中间可能有 filter 操作。

严格形式查询

严格查询是不含任何快捷方式或隐式值的查询:

  • 所有快捷方式都会被替换。
  • 所有隐式参数都会显式表示。
  • 使用全名引用列。
  • 显式指定新列的名称。
  • 任何隐式提供的校准操作都会显式提供。

使用严格形式可使查询更灵活地适应输入表结构的更改,并且可以更清晰地显示查询的作用。使用查询的严格形式并不能提高查询效率。

当您为图表或提醒政策保存查询时,它会转换为严格形式。保存操作的确认对话框会显示严格形式。

由于快捷方式和严格形式均可用,您可能会遇到看起来截然不同的等效 MQL 查询。例如,以下计算每 CPU 秒使用的已接收数据包数量的查询使用了许多快捷方式:

gce_instance
| (zone =~ ".*-a")
| {
    compute.googleapis.com/instance/network/received_packets_count ;
    compute.googleapis.com/instance/cpu/usage_time
  }
| join
| div

如果您将此查询保存为图表或保存为提醒政策的一部分,则生成的严格形式查询具有完全相同的作用。但是,严格形式看起来可能截然不同,如以下示例所示:

fetch gce_instance
| filter (resource.zone =~ '.*-a')
| { t_0:
      metric 'compute.googleapis.com/instance/network/received_packets_count'
      | align delta() ;
    t_1:
      metric 'compute.googleapis.com/instance/cpu/usage_time'
      | align delta() }
| join
| value [v_0: div(t_0.value.received_packets_count, t_1.value.usage_time)]

当您修改图表或提醒政策的已保存定义时,查询编辑器会显示严格形式。

匹配 resource.project_id

Google Cloud 项目具有显示名,显示名会显示在菜单中,但无法唯一标识项目。项目显示名可能是“Monitoring demo”。

项目还包含两个用作标识符的字段:

  • 项目 ID:具有唯一性的字符串标识符。它通常基于显示名。项目 ID 是在项目创建时设置的,通常是将项目名称的元素串联起来,并可能在末尾添加数字(如果唯一性需要如此)。项目 ID 的格式可能是“monitoring-demo”或“monitoring-demo-2349”。项目 ID 有时也称为项目名称。
  • 项目编号:具有唯一性的数字标识符。

每个受监控的资源类型都包含一个 project_id 标签,其中包含资源所属项目的项目编号的字符串表示形式以及该资源的相关数据。

在 MQL 查询中,此标签称为 resource.project_idresource.project_id 标签的值为文本形式的项目编号,但在某些情况下,MQL 会将该值转换为项目 ID。

在以下情况下,MQL 将 resource.project_id 标签的值视为项目 ID 而不是项目编号:

  • 对于 resource.project_id 标签的值,图表的图例显示项目 ID,而不是项目编号。

  • resource.project_id 的值与字符串字面量的相等性比较可识别项目编号和项目 ID。例如,以下命令会针对此项目拥有的资源返回 true:

    • resource.project_id == "monitoring-demo"
    • resource.project_id == "530310927541"

    这种情况适用于 ==!= 运算符及其函数形式 eq()ne()

  • 针对 resource.project_id 标签的正则表达式匹配可使用项目编号或项目 ID。例如,对于此项目拥有的资源,以下两个表达式均返回 true

    • resource.project_id =~ "monitoring-.*"
    • resource.project_id =~ ".*27541"

    这种情况适用于 =~!~ 运算符以及函数形式 re_full_match

对于所有其他情况,将使用 resource.project_id 标签的实际值。例如,concatenate("project-", resource.project_id) 会生成值 project-530310927541,而不是 project-monitoring-demo

比率和“边缘效应”

通常,最好根据为单个指标类型收集的时间序列使用标签值计算比率。根据两个不同指标类型计算出的比率会受到“边缘效应”的影响。

例如,假设您有两个不同的指标类型,一个是 RPC 总数,一个是 RPC 错误数量,并且您想要计算错误 RPC 与总 RPC 的比率。失败的 RPC 会计入两个指标类型的时间序列中,因此当您校准时间序列时,失败的 RPC 可能会出现在总数的某一个校准间隔中,但出现在错误数量的不同校准间隔中。导致这种差异的原因有很多,包括:

  • 由于两个不同的时间序列记录同一事件,因此有两个计数器值在实现集合,并且它们不会以原子方式更新。
  • 采样率可能会不一样。当时间序列与公共时间段校准时,单个事件的计数可能会出现在不同指标的时间序列的相邻校准间隔中。

相应校准间隔内值的数量差可能导致无意义的 error/total 比率值,例如 1/0 或 2/1。

较大数量之间的比率的边缘效应通常更小。您可以通过聚合来获得更大的数量,这可以通过使用比采样周期更长的校准窗口或针对特定标签将数据分组来实现。这些方法可以最大限度地减少给定时间间隔中数据点数量的微小差异的影响。在一个时间间隔内,预期数据点数量为 3 时的两点间的差异会比预期数量为 300 时更明显。

如果您使用的是内置指标类型,则可能只能计算不同指标类型的比率以获得所需的值。

如果您设计的自定义指标可能会在两个不同的指标中统计相同的内容(例如,返回错误状态的 RPC 数量),请考虑使用单个指标,以使每个计数仅被包含一次。例如,如果您要统计 RPC 数量并跟踪失败的 RPC 与所有 RPC 的比率,请创建单个指标类型来统计 RPC 数量,然后使用标签来记录调用状态,包括“成功”状态。然后,通过更新相应的计数器来记录每个状态值(“错误”或“成功”)。

MQL 日期格式

MQL 目前仅支持有限数量的日期格式。在 MQL 查询中,日期的表示形式为以下之一:

  • d'BASE_STRING'
  • D'BASE_STRING'

BASE_STRING2010/06/23-19:32:15-07:00 形式的字符串。分隔日期和时间的第一个短划线 (-) 可以替换为空格。在时间组成部分中,时钟时间 (19:32:15) 的某些部分或时区说明符 (-07:00) 可以舍弃。

以下示例是 MQL 查询中的有效日期:

  • d'2010/06/23-19:32:15-07:00'
  • d'2010/06/23 19:32:15-07:00'
  • d'2010/06/23 19:32:15'
  • D'2010/06/23 19:32'
  • d'2010/06/23-19'
  • D'2010/06/23 -07:00'

下表列出了 BASE_STRING 的语法:

结构 含义
%Y/%m/%d 日期
%Y/%m/%d %H
%Y/%m/%d-%H
日期、小时
%Y/%m/%d %H:%M
%Y/%m/%d-%H:%M
日期、小时、分钟
%Y/%m/%d %H:%M:%S
%Y/%m/%d-%H:%M:%S
日期、小时、分钟、秒
%Y/%m/%d %H:%M:%E*S
%Y/%m/%d-%H:%M:%E*S
日期、小时、分钟、小数秒
%Y/%m/%d %Ez 带时区的日期
%Y/%m/%d %H%Ez
%Y/%m/%d-%H%Ez
带时区的日期、小时
%Y/%m/%d %H:%M%Ez
%Y/%m/%d-%H:%M%Ez
带时区的日期、小时、分钟
%Y/%m/%d %H:%M:%S%Ez
%Y/%m/%d-%H:%M:%S%Ez
带时区的日期、小时、分钟、秒
%Y/%m/%d %H:%M:%E*S%Ez
%Y/%m/%d-%H:%M:%E*S%Ez
带时区的日期、小时、分钟、小数秒

查询的长度和复杂度

Monitoring Query Language 查询可能很长且复杂,但也具有一些限制。

  • 编码为 UTF-8 的查询文本不得超过 10000 个字节。
  • 查询不得超过 2000 个语言构造;也就是说,AST 复杂度的上限为 2000 个节点。

抽象语法树(也称为 AST)是源代码(在本例中为 MQL 查询字符串)的一种表示法,树中的节点映射到代码中的语法结构。

MQL 宏

MQL 包含一个宏定义实用程序,即 def。您可以用 MQL 宏定义实用程序来替换重复操作,方便读取复杂查询,从而简化查询开发。您可以为表操作和函数定义宏。

用于表格操作的宏

您可以编写宏以执行新的表操作。一般语法如下所示:

def MACRO_NAME [MACRO_PARAMETER[, MACRO_PARAMETER]] = MACRO_BODY ;

要调用宏,请使用以下语法:

@MACRO_NAME [MACRO_ARG [, MACRO_ARG]]

例如,假设您使用以下查询来提取 CPU 利用率数据:

fetch gce_instance::compute.googleapis.com/instance/cpu/utilization
| every 1m
| group_by [zone], mean(val())

可以使用以下宏替换第一行:

def my_fetch = fetch gce_instance::compute.googleapis.com/instance/cpu/utilization ;

如需在查询中调用宏,请按如下所示替换原始 fetch

def my_fetch = fetch gce_instance::compute.googleapis.com/instance/cpu/utilization ;

@my_fetch
| every 1m
| group_by [zone], mean(val())

您可以用接受参数的宏替换第二行和第三行。宏定义列出了宏的参数,在宏正文中,宏的参数为 $MACRO_PARAMETER。例如,您可以定义以下宏:

def my_every time_arg = every $time_arg ;

def my_group label, aggr = group_by [$label], $aggr ;

要调用这些宏并提供参数,请在宏调用的逗号分隔列表中指定参数。该查询显示了包含所有已定义宏及其调用的查询:

def my_fetch = fetch gce_instance::compute.googleapis.com/instance/cpu/utilization ;
def my_every time_arg = every $time_arg ;
def my_group label, aggr = group_by [$label], $aggr ;

{@my_fetch}
| @my_every 1m
| @my_group zone, mean(val())

用于函数的宏

对于 MQL 函数,您可以在以逗号分隔的列表中指定任何参数。括号将函数宏与表操作宏区分开。即使没有任何参数,调用中也必须有父级出现。

def MACRO_NAME([MACRO_PARAMETER [, MACRO_PARAMETER]]) = MACRO_BODY ;

例如,以下查询检索两个指标的表,将两个表合并为包含两个值列,然后计算名为 received_percent 的列的接收字节数与总字节数的比率:

{
  fetch k8s_pod :: kubernetes.io/pod/network/received_bytes_count ;
  fetch k8s_pod :: kubernetes.io/pod/network/sent_bytes_count
}
| join
| value [received_percent: val(0) * 100 / (val(0) + val(1))]

您可以将 received_percent 计算替换为与以下示例类似的宏:

def recd_percent(recd, sent) = $recd * 100 / ($recd + $sent) ;

要调用函数宏,请使用以下语法:

@MACRO_NAME([MACRO_ARG[, MACRO_ARG]])

在调用不带任何参数的函数宏时,您必须指定空括号,以区分调用表操作宏的调用。

以下示例展示了包含比率计算宏的上一查询:

def recd_percent(recd, sent) = $recd * 100 / ($recd + $sent) ;

{
  fetch k8s_pod :: kubernetes.io/pod/network/received_bytes_count ;
  fetch k8s_pod :: kubernetes.io/pod/network/sent_bytes_count
}
| join
| value [received_percent: @recd_percent(val(0), val(1))]

限制

MQL 宏功能不支持以下各项:

  • 嵌套宏。
  • 递归定义的宏。任何宏正文都不能引用尚未完全定义的任何宏,包括其自身。
  • 将宏定义的函数用作表操作。
  • 将宏参数用作函数或表操作的名称。