3. 选择器与匹配模式:精确匹配、正则匹配、向量匹配
在 PromQL 告警表达式中,选择器决定了监控数据的检索范围,而匹配模式决定了不同时间序列之间的关联方式。理解这两者是编写精准告警规则的基础,也是避免误报、漏报的关键。
3.1 精确匹配:标签的严格筛选
精确匹配是最直接的选择器方式,要求标签键与标签值完全相等。在 PromQL 中,使用 = 操作符实现。
语法:
<metric_name>{<label_key>="<label_value>"}
示例:
# 精确匹配 instance 为 "web-01:9090" 的 HTTP 请求速率
http_requests_total{instance="web-01:9090"}
# 精确匹配 job 为 "api-server" 且 method 为 "GET" 的请求
http_requests_total{job="api-server", method="GET"}
| 操作符 | 含义 | 使用场景 |
|---|---|---|
= |
精确等于 | 已知确切标签值,如特定主机、特定接口 |
!= |
不等于 | 排除特定标签值,如排除健康检查端点 |
最佳实践:在告警表达式中,优先使用精确匹配缩小数据范围,避免因标签值意外变化导致告警范围扩大。
3.2 正则匹配:灵活的模式匹配
当需要匹配一组具有相同模式的标签值时,正则匹配提供了强大的灵活性。PromQL 支持两种正则操作符:=~(匹配正则)和 !~(不匹配正则)。
语法:
<metric_name>{<label_key>=~"<regex_pattern>"}
示例:
# 匹配所有 web 服务器(web-01, web-02, web-03...)
http_requests_total{instance=~"web-0[1-3]:9090"}
# 排除所有 staging 环境的指标
http_requests_total{environment!~"staging|dev"}
# 匹配路径以 /api 开头的请求
http_requests_total{path=~"/api/.*"}
| 操作符 | 含义 | 注意事项 |
|---|---|---|
=~ |
匹配正则表达式 | 使用 Go 标准库的 RE2 语法,不支持回溯引用 |
!~ |
不匹配正则表达式 | 注意正则性能,避免过于复杂的模式 |
性能警告:正则匹配比精确匹配消耗更多计算资源。在告警规则中,避免使用通配符
.* 作为前缀(如 .*web),优先使用后缀通配(如 web.*)或更精确的模式。
3.3 向量匹配:连接不同时间序列
向量匹配是 PromQL 二元操作符(如算术运算、比较运算)的核心机制,用于将左侧和右侧的两个时间序列进行配对计算。根据匹配的严格程度,分为三种模式。
3.3.1 One-to-One(一对一匹配)
要求左右两侧的时间序列具有完全相同的标签集合。这是默认的匹配模式,也是最严格的。
语法:
<vector1> <operator> <vector2>
示例:
# 计算每个实例的 CPU 使用率(假设两个指标标签完全一致)
rate(node_cpu_seconds_total{mode="idle"}[5m])
/
rate(node_cpu_seconds_total[5m])
| 条件 | 结果 |
|---|---|
| 左侧标签集 = 右侧标签集 | 配对计算,产生一条结果 |
| 左侧标签集 ≠ 右侧标签集 | 不匹配,丢弃该数据点 |
3.3.2 Many-to-One(多对一匹配)与 Group-Left/Right
当一侧有多个时间序列,另一侧只有一个时,需要使用 group_left 或 group_right 关键字进行多对一匹配。这在计算比例、聚合统计时非常有用。
语法:
<vector1> <operator> on(<labels>) group_left(<extra_labels>) <vector2>
示例:
# 计算每个实例的请求占比(按 job 分组)
# 左侧:每个实例的请求数(多)
# 右侧:每个 job 的总请求数(一)
http_requests_total
/ on(job) group_left()
sum by(job) (http_requests_total)
| 关键字 | 含义 | 使用场景 |
|---|---|---|
on(<labels>) |
指定用于匹配的标签 | 只关心部分标签的匹配 |
group_left |
左侧为多,右侧为一 | 左侧保留所有标签,右侧聚合 |
group_right |
右侧为多,左侧为一 | 右侧保留所有标签,左侧聚合 |
ignoring(<labels>) |
忽略指定标签进行匹配 | 排除不关心的标签差异 |
实战技巧:在告警规则中,
group_left 常用于计算实例级别的异常比例。例如,当某个 job 下超过 50% 的实例出现高延迟时触发告警,而不是每个实例单独告警。
3.4 匹配模式在告警中的工程实践
在编写告警表达式时,选择合适的匹配模式直接影响告警的准确性和计算效率。以下是一些经过验证的工程实践。
3.4.1 使用 on() 缩小匹配范围
当两个指标具有不同的标签集,但共享部分关键标签时,使用 on() 指定匹配标签,避免因多余标签导致匹配失败。
# 错误:标签集不匹配,不会产生结果
node_memory_MemTotal_bytes / node_memory_MemFree_bytes
# 正确:只按 instance 标签匹配
node_memory_MemTotal_bytes
/ on(instance)
node_memory_MemFree_bytes
3.4.2 使用 ignoring() 排除干扰标签
当两个指标大部分标签相同,但存在少数无关标签时,使用 ignoring() 忽略这些标签。
# 忽略 mode 标签,计算所有 CPU 模式的总使用率
rate(node_cpu_seconds_total[5m])
/ ignoring(mode)
sum by(instance, cpu) (rate(node_cpu_seconds_total[5m]))
3.4.3 告警收敛中的向量匹配
在告警收敛规则中,向量匹配常用于实现聚合告警,避免告警风暴。
# 场景:当 job="api" 中超过 30% 的实例延迟 > 1s 时触发告警
# 第一步:计算每个实例的延迟是否超标(0 或 1)
instance_high_latency = http_request_duration_seconds > 1
# 第二步:计算每个 job 中超标实例的比例
sum by(job) (instance_high_latency)
/ on(job) group_left()
count by(job) (http_request_duration_seconds)
> 0.3
关键原则:
- 精确优先:能用
=就不用=~,能用on()就不用默认全标签匹配。 - 收敛优先:在告警规则中,优先使用
group_left实现多对一聚合,减少告警数量。 - 测试优先:在 Prometheus 的 Graph 页面或使用
promtool测试匹配结果,确保符合预期。
3.5 常见陷阱与避坑指南
| 陷阱 | 现象 | 解决方案 |
|---|---|---|
| 标签集不匹配 | 二元操作返回空结果 | 使用 on() 或 ignoring() 显式指定匹配标签 |
| 正则过于宽泛 | 匹配到意外的时间序列 | 使用 ^ 和 $ 锚定,避免前缀通配 |
| group_left 忘记指定 on() | 匹配失败或结果不符合预期 | 始终与 on() 或 ignoring() 配合使用 |
| 忽略标签顺序 | 标签顺序不影响匹配,但影响可读性 | 保持标签顺序一致,便于调试 |
重要提醒:在告警规则中使用正则匹配时,务必在 Prometheus 的 Graph 页面中验证正则表达式是否按预期工作。一个错误的正则可能导致告警范围扩大或缩小,造成严重事故。