Files
wiki/监控/Prometheus/Prometheus-学习手册-02-PromQL与查询篇.md
T
2026-10-08 15:05:32 +08:00

2844 lines
131 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Prometheus 知识学习手册 · 第 2 篇:PromQL 与查询
> 基于 [prometheus.io 官方文档](https://prometheus.io/docs/prometheus/latest/querying/basics/) 整理
> 文档版本:Prometheus v3.15.0(最新稳定版,2026-10 发布)| 抓取日期:2026-10-08
> 数据来源:prometheus/prometheus@v3.15.0、prometheus/alertmanager、prometheus/docs 官方仓库源文件(与官网 latest 内容一致)
>
> **全册结构(共 4 篇)**
> - 第 1 篇:入门与架构
> - **第 2 篇:PromQL 与查询** ← 本篇
> - 第 3 篇:配置与运维
> - 第 4 篇:Alertmanager 与生态(告警、插桩、可视化、规范)
---
## 目录
- [第 1 章 PromQL 基础](#第-1-章-promql-基础)
- [1.1 查询语言总览](#11-查询语言总览)
- [1.2 样本与 flavor](#12-样本与-flavor)
- [1.3 表达式语言数据类型](#13-表达式语言数据类型)
- [1.4 字面量与时间表达式](#14-字面量与时间表达式)
- [1.5 时间序列选择器](#15-时间序列选择器)
- [1.6 时间修饰:offset、@ modifier 与子查询](#16-时间修饰offset-modifier-与子查询)
- [1.7 Lookback 与 staleness](#17-lookback-与-staleness)
- [1.8 正则表达式](#18-正则表达式)
- [1.9 UTF-8 引用语法提点](#19-utf-8-引用语法提点)
- [1.10 注释与慢查询规避](#110-注释与慢查询规避)
- [第 2 章 运算符](#第-2-章-运算符)
- [2.1 一元运算符](#21-一元运算符)
- [2.2 算术运算符](#22-算术运算符)
- [2.3 三角函数运算符](#23-三角函数运算符)
- [2.4 直方图裁剪运算符](#24-直方图裁剪运算符)
- [2.5 比较运算符](#25-比较运算符)
- [2.6 逻辑/集合运算符](#26-逻辑集合运算符)
- [2.7 向量匹配](#27-向量匹配)
- [2.8 聚合运算符](#28-聚合运算符)
- [2.9 二元运算符优先级](#29-二元运算符优先级)
- [2.10 实验特性:fill modifiers](#210-实验特性fill-modifiers)
- [第 3 章 函数全表](#第-3-章-函数全表)
- [3.1 通用约定](#31-通用约定)
- [3.2 缺失检测类](#32-缺失检测类)
- [3.3 取整与数值钳制类](#33-取整与数值钳制类)
- [3.4 数学函数](#34-数学函数)
- [3.5 时间与日期类](#35-时间与日期类)
- [3.6 范围向量(趋势/变化/速率)类](#36-范围向量趋势变化速率类)
- [3.7 直方图专用函数](#37-直方图专用函数)
- [3.8 标签操作类](#38-标签操作类)
- [3.9 排序与向量构造类](#39-排序与向量构造类)
- [3.10 over_time 聚合系列](#310-over_time-聚合系列)
- [3.11 三角函数](#311-三角函数)
- [3.12 函数速查总表](#312-函数速查总表)
- [第 4 章 查询示例集](#第-4-章-查询示例集)
- [4.1 简单时序选择](#41-简单时序选择)
- [4.2 子查询示例](#42-子查询示例)
- [4.3 函数与运算符实战](#43-函数与运算符实战)
- [第 5 章 HTTP API](#第-5-章-http-api)
- [5.1 通用格式与响应信封](#51-通用格式与响应信封)
- [5.2 表达式查询端点](#52-表达式查询端点)
- [5.3 元数据查询端点](#53-元数据查询端点)
- [5.4 目标与抓取池端点](#54-目标与抓取池端点)
- [5.5 规则与告警端点](#55-规则与告警端点)
- [5.6 指标元数据端点](#56-指标元数据端点)
- [5.7 Alertmanager 与状态端点](#57-alertmanager-与状态端点)
- [5.8 TSDB 管理 API](#58-tsdb-管理-api)
- [5.9 写入/读取接收端](#59-写入读取接收端)
- [5.10 通知与特性端点](#510-通知与特性端点)
- [5.11 Remote Read API](#511-remote-read-api)
- [第 6 章 查询调试](#第-6-章-查询调试)
- [第 7 章 直方图与分位数](#第-7-章-直方图与分位数)
- [第 8 章 UTF-8 指标名](#第-8-章-utf-8-指标名)
- [附录 本篇覆盖清单](#附录-本篇覆盖清单)
---
## 第 1 章 PromQL 基础
### 1.1 查询语言总览
Prometheus 提供函数式查询语言 **PromQL**(Prometheus Query Language),用于实时选择与聚合时间序列数据。
查询请求分为两类:
| 类型 | 含义 | UI 对应 |
|:-----|:-----|:--------|
| **instant query**(瞬时查询) | 在单一时点上对表达式求值 | expression browser 的 "Table" 标签页 |
| **range query**(范围查询) | 在 `start` 与 `end` 之间以等间隔 `step` 上求值 | expression browser 的 "Graph" 标签页 |
PromQL 在两种情况下行为完全一致——range query 相当于在多个不同时间戳上重复执行 instant query。其他程序可通过 **HTTP API** 获取 PromQL 表达式结果(见第 5 章)。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.2 样本与 flavor
#### 1.2.1 样本值类型
- **float sample**:简单的浮点数值。
- **native histogram sample**:包含完整直方图,即 count、sum 与 buckets。
术语说明(重要,避免混淆):
- 术语 "histogram sample" **始终**指 native histogram。
- **classic histogram** 指由带 `_bucket`、`_count`、`_sum` 后缀的 float 样本序列共同描述的直方图。从 PromQL 视角看它们只是 float samples,**不存在 "classic histogram samples"**。
#### 1.2.2 counter/gauge flavor
float sample 与 histogram sample 都具有 counter 或 gauge 的 **flavor**:
| 类型 | 命名 | 是否存储 flavor |
|:-----|:-----|:----------------|
| counter float sample | "counters" | 否(float samples 不存储 flavor) |
| gauge float sample | "gauges" | 否 |
| counter histogram | "counter histograms" | 是(histogram sample "知道" 自己的 flavor) |
| gauge histogram | "gauge histograms" | 是 |
因为 float samples 不存储 flavor,需用户自行在写 PromQL 时考虑(约定:float counter 的时间序列名以 `_total` 结尾以便区分)。而 histogram samples 知道自己属于 counter 还是 gauge,因此可对不匹配的操作给出**可靠警告**:
- 对 gauge floats 应用 `rate` 函数很可能产生荒谬结果,但查询会被**无抱怨地**处理。
- 若应用于 gauge histograms,查询结果会被标注 **warning**。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.3 表达式语言数据类型
表达式或子表达式可求值为以下四种类型之一:
| 类型 | 英文 | 说明 |
|:-----|:-----|:-----|
| **瞬时向量** | Instant vector | 一组时间序列,每条序列含单个样本,全部共享同一时间戳 |
| **区间向量** | Range vector | 一组时间序列,每条序列含一段时间范围内的多个数据点 |
| **标量** | Scalar | 简单数值浮点值 |
| **字符串** | String | 简单字符串值;**目前未使用** |
**哪些类型可作表达式根?**
- 对 **instant queries**:以上任意类型均可作为表达式根。
- 对 **range queries**:**仅支持 scalar 类型与 instant-vector 类型**的表达式。
向量与时间序列均可混合包含 float samples 与 histogram samples。
#### 1.3.1 Native histogram bucket layout 的 reconciliation
- Native histograms 可有不同 bucket layouts,但通常可转换为兼容版本以施加二元与聚合运算。作用于 range vectors 且适用于 native histograms 的函数也执行此类 reconciliation。
- 二元运算中 reconciliation **成对(pairwise)**执行;聚合运算与函数中所有 histogram samples 被统一 reconcile 到一种兼容 bucket layout。
- 并非所有 bucket layout 都可 reconcile;若运算遇到不兼容直方图,对应**输出向量元素会从结果中移除**,并标记 warn-level annotation。详见 native histogram specification。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.4 字面量与时间表达式
#### 1.4.1 String literals(字符串字面量)
**不存在 "histogram literal"。** String literals 由单引号、双引号或反引号界定,遵循与 Go 相同的 escaping rules:
- 单/双引号内反斜杠开始转义序列,后可跟 `a`、`b`、`f`、`n`、`r`、`t`、`v` 或 `\`;特定字符可用八进制(`\nnn`)或十六进制(`\xnn`、`\unnnn`、`\Unnnnnnnn`)表示。
- 反引号界定的字符串中**不解析转义字符**;且与 Go 不同,Prometheus **不丢弃**反引号内的换行。
```promql
"this is a string"
'these are unescaped: \n \\ \t'
`these are not unescaped: \n ' " \t`
```
#### 1.4.2 Float literals
标量浮点值可写为整数或浮点字面量,格式(空白仅为可读性):
```text
[-+]?(
[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
| 0[xX][0-9a-fA-F]+
| [nN][aA][nN]
| [iI][nN][fF]
)
```
示例:`23`、`-2.43`、`3.4e-9`、`0x8f`、`-Inf`、`NaN`。可在十进制或十六进制数字之间使用下划线 `_` 提升可读性,如 `1_000_000`、`.123_456_789`、`0x_53_AB_F3_82`。
#### 1.4.3 时间持续(duration)
Float literals 也用于以**秒**为单位指定时长。十进制整数可组合以下时间单位:
| 单位 | 含义 | 等价关系 |
|:-----|:-----|:---------|
| `ms` | milliseconds | — |
| `s` | seconds | 1s equals 1000ms |
| `m` | minutes | 1m equals 60s(忽略 leap seconds) |
| `h` | hours | 1h equals 60m |
| `d` | days | 1d equals 24h(忽略 daylight saving time) |
| `w` | weeks | 1w equals 7d |
| `y` | years | 1y equals 365d(忽略 leap days) |
给十进制整数加上述单位后缀,等价于以裸 float literal 表示的相同秒数:
| 写法 | 等价裸值 |
|:-----|:---------|
| `1s` | `1.` |
| `2m` | `120.` |
| `1ms` | `0.001.` |
| `-2h` | `-7200.` |
以下示例**不**工作:
- `0xABm`(十六进制数不可加后缀)
- `1.5h`(时间单位不能与浮点数组合)
- `+Infd`(±Inf 或 NaN 不可加后缀)
多个单位可通过拼接带后缀整数组合,**单位必须从最长到最短排序,且每个单位在一个 float literal 中只能出现一次**:
| 写法 | 等价 |
|:-----|:-----|
| `1h30m` | 5400s 即 `5400.` |
| `12h34m56s` | 45296s 即 `45296.` |
| `54s321ms` | `54.321` |
#### 1.4.4 Duration expressions(时长表达式)
凡期望时间持续之处均可使用算术表达式,即 **range vector selectors** 与 **offset durations** 中:
```promql
rate(http_requests_total[5m * 2]) # 10 minute range
rate(http_requests_total[(5+2) * 1m]) # 7 minute range
http_requests_total offset (1h / 2) # 30 minute offset
http_requests_total offset ((2 ^ 3) * 1m) # 8 minute offset
```
使用 `offset` 配合 duration expression 时**必须用括号包裹表达式**;无括号时 offset 计算只采用第一个 duration 值。支持的运算符(按通常优先级):`+` 加法、`-` 减法、`*` 乘法、`/` 除法、`%` 取模、`^` 幂。
duration expressions 内可用函数:
| 函数 | 解析结果 | instant query 下 |
|:-----|:---------|:-----------------|
| `step()` | range query 的 step width | `0s` |
| `range()` | range query 的完整范围(end time − start time) | `0s` |
| `min_of(<duration>, <duration>)` | 两者中较小者(用于把时长封顶到最大值) | — |
| `max_of(<duration>, <duration>)` | 两者中较大者(用于强制最小值) | — |
`range()` 特别适合配合 `@ end()` 回顾整个查询范围,如 `max_over_time(metric[range()] @ end())`。
例如 `max_of(step(), 5s)` 保证时长不短于 `5s`,`min_of(range(), 1h)` 把时长封顶为 `1h`。**注意**:`@` modifier 中**不支持** duration expressions。
等价时长示例:
| 表达式 | 等价于 |
|:-------|:-------|
| `5m * 2` | `10m` 或 `600s` |
| `10m - 1m` | `9m` 或 `540s` |
| `(5+2) * 1m` | `7m` 或 `420s` |
| `1h / 2` | `30m` 或 `1800s` |
| `4h % 3h` | `1h` 或 `3600s` |
| `(2 ^ 3) * 1m` | `8m` 或 `480s` |
| `step() + 1` | 查询 step width 增加 `1s` |
| `max_of(step(), 5s)` | 查询 step 与 `5s` 中较大者 |
| `min_of(2 * step() + 5s, 5m)` | 两倍 step 加 `5s` 与 `5m` 中较小者 |
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.5 时间序列选择器
#### 1.5.1 Instant vector selectors(瞬时向量选择器)
允许选择一组时间序列及其在给定时点的单个样本值。最简形式只指定 metric name,返回包含所有该名字时间序列的 instant vector。
返回值是**查询评估时间戳(instant query)或查询中当前 step(range query)之前或等于该时刻的最新样本**。`@` modifier 可覆盖选择所依据的时间戳。仅当其最新样本在 lookback period 之内时序列才被返回(见 1.7)。
```promql
http_requests_total
http_requests_total{job="prometheus",group="canary"}
http_requests_total{environment=~"staging|testing|development",method!="GET"}
```
#### 1.5.2 标签匹配运算符
| 运算符 | 含义 |
|:-------|:-----|
| `=` | 选择标签值**精确等于**所给字符串 |
| `!=` | 选择标签值**不等于**所给字符串 |
| `=~` | 选择标签值**正则匹配**所给字符串 |
| `!~` | 选择标签值**不正则匹配**所给字符串 |
关键规则:
- Regex 匹配**完全锚定**:`env=~"foo"` 视为 `env=~"^foo$"`。
- 匹配空标签值的 matcher 也会选中**所有根本没有设置该标签**的时间序列。
- 同一标签名可有多个 matcher,**所有 matcher 都通过**才返回。
**空标签匹配示例**:给定数据集 `http_requests_total`、`http_requests_total{replica="rep-a"}`、`http_requests_total{replica="rep-b"}`、`http_requests_total{environment="development"}`:
- 查询 `http_requests_total{environment=""}` 返回前三条,排除 `environment="development"` 那条。
- 查询 `http_requests_total{replica!="rep-a",replica=~"rep.*"}` 匹配 `http_requests_total{replica="rep-b"}`。
**合法性要求**:Vector selectors **必须指定 name 或至少一个不匹配空字符串的 label matcher**:
| 写法 | 是否合法 |
|:-----|:---------|
| `{job=~".*"} # Bad!` | ❌ 非法(正则匹配空串) |
| `{job=~".+"} # Good!` | ✅ 合法 |
| `{job=~".*",method="get"} # Good!` | ✅ 合法(有一个不匹配空串) |
#### 1.5.3 通过 `__name__` 匹配指标名
Label matchers 也可通过匹配内部 `__name__` 标签施加于 metric name:
- `http_requests_total` 等价于 `{__name__="http_requests_total"}`。
- 除 `=` 外还可用 `!=`、`=~`、`!~`。
- `{__name__=~"job:.*"}` 选择所有名字以 `job:` 开头的指标。
metric name 不得为关键字 `bool`、`on`、`ignoring`、`group_left`、`group_right`,例如 `on{} # Bad!` 非法;变通方法是使用 `__name__` 标签:`{__name__="on"} # Good!`。
#### 1.5.4 Range Vector Selectors(区间向量选择器)
行为类似 instant vector literals,但选择从当前时刻回溯的一段样本范围。语法上在 vector selector 末尾用方括号 `[]` 追加一个 float literal,指定每条 range vector 元素向前回溯多少秒取值,常用带时间单位的写法如 `[5m]`。
- **区间为左开右闭**:时间戳与左边界重合的样本被**排除**,与右边界重合的被**包含**。
```promql
http_requests_total{job="prometheus"}[5m]
```
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.6 时间修饰:offset、@ modifier 与子查询
#### 1.6.1 Offset modifier
允许为查询中单个 instant 与 range vector 改变时间偏移。**`offset` 必须紧跟 selector**。
```promql
http_requests_total offset 5m
sum(http_requests_total{method="GET"} offset 5m) // GOOD.
sum(http_requests_total{method="GET"}) offset 5m // INVALID.
rate(http_requests_total[5m] offset 1w)
rate(http_requests_total[5m] offset -1w)
```
- 查询过去的样本用**正** offset。
- **负 offset** 可实现向未来方向的时间比较,允许查询前瞻其评估时间。
#### 1.6.2 @ modifier
允许为查询中单个 instant 与 range vector 改变**求值时间**。`@` 接受 Unix timestamp,用 float literal 描述。**`@` 必须紧跟 selector**。
```promql
http_requests_total @ 1609746000
sum(http_requests_total{method="GET"} @ 1609746000) // GOOD.
sum(http_requests_total{method="GET"}) @ 1609746000 // INVALID.
rate(http_requests_total[5m] @ 1609746000)
```
- 它支持上述所有数值字面量表示形式。
- 可与 `offset` 共用,offset 相对于 `@` 时间施加,**结果与两者顺序无关**:
```promql
http_requests_total @ 1609746000 offset 5m
http_requests_total offset 5m @ 1609746000
```
- `start()` 与 `end()` 可作为 `@` 的特殊值:对 range query 分别解析为 range query 的起点与终点,且对所有 step 保持不变;对 instant query 两者都解析为求值时间。
```promql
http_requests_total @ start()
rate(http_requests_total[5m] @ end())
```
**注意**:`@` modifier 允许查询前瞻其评估时间。
#### 1.6.3 Subquery(子查询)
Subquery 允许对给定 range 与 resolution 运行 instant query,结果是一个 **range vector**。
语法:
```text
<instant_query> '[' <range> ':' [<resolution>] ']' [ @ <float_literal> ] [ offset <float_literal> ]
```
- `<resolution>` 可选,**默认为全局 evaluation interval**。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.7 Lookback 与 staleness
#### 1.7.1 为什么需要 lookback
查询期间采样数据的时间戳独立于实际现存的时间序列数据而选择,主要为支持 `sum`、`avg` 等聚合场景(多条被聚合序列在时间上并不精确对齐)。因此 Prometheus 需为每个相关时间序列在这些时间戳赋值,方式是取**不超过 lookback period 之前的最新样本**。
#### 1.7.2 lookback period
| 项 | 值 |
|:---|:---|
| 默认值 | **5 分钟** |
| 全局配置 | `--query.lookback-delta` flag |
| 单次查询覆盖 | `lookback_delta` 参数(HTTP API,见第 5 章) |
#### 1.7.3 Staleness(陈旧标记)
- 若 target scrape 或 rule evaluation 不再返回某条先前存在的时间序列的样本,该序列被标记为 **stale**。
- 若 target 被移除,先前抓取的序列会在移除后不久被标记 stale。
- 若查询在某序列被标记 stale **之后**的采样时间戳求值,则该序列**不返回值**;若后续有新样本摄入,则按预期返回。
- 序列在其不再被导出或 target 不存在时变 stale,会**消失在图中**(在最后一次采集样本处),并在标记 stale 后不再被查询返回。
- 某些自行给样本打时间戳的 exporters 行为不同:停止导出的序列会在(默认)5 分钟内保留最后一个值再消失;`track_timestamps_staleness` 设置可改变该行为。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.8 正则表达式
Prometheus 中所有正则表达式使用 **RE2 syntax**。Regex 匹配**始终完全锚定**。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
### 1.9 UTF-8 引用语法提点
从 Prometheus 3.0 起,任意 UTF-8 字符串都是合法的 metric/label 名(详见第 8 章)。在 PromQL 中查询这类名称需要**略不同的语法**:
- classic 查询语法对 legacy-compatible 名称仍有效:`my_metric{}`。
- UTF-8 名称**必须加引号并移入花括号内**:`{"my.metric"}`。
- label 名若含 legacy-incompatible 字符也必须加引号:`{"metric.name", "my.label.name"="bar"}`。
- metric 名可出现在花括号内任意位置,但风格上偏好放在首个位置。
> 引自:[UTF-8 in Prometheus](https://prometheus.io/docs/guides/utf8/)
### 1.10 注释与慢查询规避
#### 1.10.1 注释
PromQL 支持以 `#` 开头的行注释,例如 `# This is a comment`。
#### 1.10.2 Avoiding slow queries and overloads
若查询需处理大量数据,绘图可能超时或拖垮服务器/浏览器。因此:
- 在未知数据上构建查询时,应**始终先在 expression browser 的表格视图中构建**,直到结果集合理(**最多数百条**而非数千条时间序列)。
- 只有充分过滤或聚合后才切到 graph 模式。
- 若临时绘图仍太慢,通过 **recording rule** 预先记录。
此点对 PromQL 尤其相关:
- 裸 metric name selector 如 `api_http_requests_total` 可能展开为**数千条**不同标签的时间序列;
- 即便输出仅少量序列,对众多序列聚合的表达式也会给服务器带来负载,类似关系数据库中求整列之和即便输出单值也会很慢。
> 引自:[Querying basics](https://prometheus.io/docs/prometheus/latest/querying/basics/)
> 💡 补充:本手册第 6 章「查询调试」给出了 query log 的开启方式与性能排查思路,可与本节配合使用。
---
## 第 2 章 运算符
PromQL 支持 **unary(一元)**、**binary(二元)** 与 **aggregation(聚合)** 三类运算符。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.1 一元运算符
PromQL 中唯一的 unary 运算符是 `-`(unary minus),可作用于 scalar 或 instant vector:
- 作用于 scalar 时返回符号取反的 scalar;
- 作用于 instant vector 时返回每个元素符号取反的 instant vector;
- histogram sample 的符号通过反转所有 bucket populations 及观测的 count 与 sum 的符号来反转,**结果 histogram sample 总被视为 gauge histogram**。
> **NOTE**:含任何**负 bucket population** 或**负观测 count** 的 histogram 只应作为**中间结果**使用。若这种负直方图是 recording rule 的最终结果,rule evaluation 会**失败**。负直方图无法被任何交换格式(exposition、remote-write、OTLP)表示,因此无法以任何方式摄入 Prometheus,只能由 PromQL 表达式创建。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.2 算术运算符
#### 2.2.1 运算符清单
| 运算符 | 含义 |
|:-------|:-----|
| `+` | addition(加法) |
| `-` | subtraction(减法) |
| `*` | multiplication(乘法) |
| `/` | division(除法) |
| `%` | modulo(取模) |
| `^` | power / exponentiation(幂) |
定义为 scalar/scalar、vector/scalar、vector/vector 值对之间的运算,遵循通常的 **IEEE 754 浮点算术**,包括对 `NaN`、`+Inf`、`-Inf` 等特殊值的处理。
#### 2.2.2 scalar 之间
直接求值为对两个操作数施加运算符后的另一个 scalar。
#### 2.2.3 instant vector 与 scalar 之间
运算符施加于 vector 中每个数据样本的值。
- 若样本是 **float**,则在该 float 与 scalar 间执行运算(例如 float samples 的 instant vector 乘 2,结果是每个样本值都乘以 2 的 float samples 向量)。
- 对 **histogram sample** 的向量元素:
- `*`:将所有 bucket populations 及观测 count 与 sum 乘以 scalar(scalar 为负则结果视为 gauge histogram,否则保留输入的 counter vs. gauge flavor)。
- `/`:要求 histogram sample 在**左侧(LHS)**、scalar 在**右侧(RHS)**,将 bucket populations 及 count 与 sum 除以 scalar。**除以零**结果为无 regular buckets 的直方图,zero bucket population 与 count、sum 依输入值(正、负、零/NaN)分别设为 `+Inf`、`-Inf` 或 `NaN`(scalar 为负则结果为 gauge histogram,否则保留输入 flavor)。
- scalar 在 LHS、histogram 在 RHS 的 `/`,以及所有其他 scalar 与 histogram 组合的算术二元运算符,**无结果且对应元素从结果向量中移除**,并以 **info-level annotation** 标记。
#### 2.2.4 两个 instant vectors 之间
运算符施加于 LHS 向量每个条目及其在 RHS 中的 matching element,结果传播到结果向量且 grouping labels 成为输出标签集。默认情况下,在对面向量找不到匹配条目的序列**不进入结果**,该行为可用 fill modifiers 调整(见 2.10)。
样本类型匹配规则:
| LHS / RHS | 行为 |
|:----------|:-----|
| float / float | 对两个输入值施加算术运算符 |
| float / histogram | 遵循 scalar 与 histogram 间的同一逻辑(`*` 与 `/` 后者要求 histogram 在 LHS 有效,其余导致元素移除) |
| histogram / histogram | **只有 `+` 与 `-` 有效**,各将匹配的 bucket populations 及 count 与 sum 相加或相减;其余运算导致元素移出输出向量并标记 info-level annotation |
关于 histogram 的加减:
- `+` 与 `-` 一般只应作用于 **gauge histograms**,但 PromQL 也允许用于 **counter histograms** 以覆盖特定用例,此时需特别留意 **counter resets 不对齐**问题(PromQL 能检测某些 counter reset 不兼容并标记 warn-level annotation)。
- 两个 counter histograms 相加结果为 **counter histogram**;**所有其他操作数组合及所有减法结果为 gauge histogram**。
- **任何涉及向量的算术二元运算都会丢弃 metric name**,即便在 `on` 中显式提到 `__name__` 也如此(参见 issue 16631)。
- 对任何可能产生负直方图的算术二元运算,须注意 2.1 节 unary operator 的相关 note。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.3 三角函数运算符
存在以下三角函数**二元**运算符,以**弧度**工作:
| 运算符 | 含义 |
|:-------|:-----|
| `atan2` | 基于 <https://pkg.go.dev/math#Atan2> |
三角函数运算符允许借助 vector matching 对两个向量执行三角函数,这是普通函数所不具备的。其行为方式与算术运算符相同,**仅作用于 float samples**;涉及 histogram samples 的运算导致对应向量元素移出输出向量并标记 **info-level annotation**。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.4 直方图裁剪运算符
存在以下二元 **histogram trim** 运算符:
| 运算符 | 含义 |
|:-------|:-----|
| `</` | trim upper:移除所有高于阈值的观测 |
| `>/` | trim lower:移除所有低于阈值的观测 |
定义为 vector/scalar 与 vector/vector 值对之间的运算,**左侧为 native histogram(exponential 或 NHCB),右侧为 float 阈值**。
- 若阈值未与直方图某 bucket 边界对齐,则采用**线性插值**(对 NHCB 与 exponential histogram 的 zero buckets)或**指数插值**(对 exponential histogram 的非零 bucket)计算阈值所在 bucket 中剩余观测的估计数量。
- 当有观测被 trim 时,新的观测值之和基于剩余观测(近似)重新计算。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.5 比较运算符
#### 2.5.1 运算符清单
| 运算符 | 含义 |
|:-------|:-----|
| `==` | equal(等于) |
| `!=` | not-equal(不等于) |
| `>` | greater-than(大于) |
| `<` | less-than(小于) |
| `>=` | greater-or-equal(大于等于) |
| `<=` | less-or-equal(小于等于) |
定义为 scalar/scalar、vector/scalar、vector/vector 值对之间。**默认过滤(filter)**,行为可通过在运算符后提供 **`bool`** 修改,`bool` 会返回值 `0` 或 `1` 而非过滤。
#### 2.5.2 各类组合的详细行为
| 组合 | 行为 |
|:-----|:-----|
| **两个 scalars** | **必须提供 `bool` modifier**,结果为另一个 scalar,依比较结果为 `0`(`false`)或 `1`(`true`) |
| **instant vector 与 scalar** | 施加于 vector 中每个数据样本值,比较结果为 false 的向量元素从结果向量中丢弃。**仅适用于 float samples**;对 histogram samples,对应元素从结果向量移除并标记 info-level annotation |
| **两个 instant vectors** | 默认作为 filter 施加于匹配条目。表达式不为真或在另一侧找不到匹配的向量元素从结果中丢弃,其余传播到结果向量且 grouping labels 成为输出标签集 |
向量间样本类型:
- 两个 float samples 匹配照常工作;
- float sample 与 histogram sample 匹配**非法**,对应元素移除并标记 info-level annotation;
- 两个 histogram samples 之间 **`==` 与 `!=` 照常工作**,**其余比较二元运算均非法**。
#### 2.5.3 `bool` modifier 与 metric name
- **涉及向量的比较二元运算中提供 `bool` modifier** 会改变行为:在另一侧找到匹配但表达式为假的向量元素值为 `0`,找到匹配且表达式为真的值为 `1`(注意无匹配或涉及 histogram samples 的非法运算**仍返回无结果**而非 `0`);且 **metric name 被丢弃**。
- 若**未提供 `bool` modifier**,则**保留左侧的 metric name**,但有例外:
- 使用 `on` 时 metric name 被丢弃;
- 使用 `group_right` 时保留右侧 metric name 以避免冲突。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.6 逻辑/集合运算符
以下 logical/set 二元运算符**仅在 instant vectors 之间定义**:
| 运算符 | 含义 |
|:-------|:-----|
| `and` | intersection(交集) |
| `or` | union(并集) |
| `unless` | complement(补集) |
语义:
- `vector1 and vector2`:结果为 `vector1` 中那些在 `vector2` 存在标签集完全匹配元素的元素,其他元素丢弃;metric name 与值从**左侧**向量带过来。
- `vector1 or vector2`:结果为包含 `vector1` 全部原始元素(标签集 + 值),外加 `vector2` 中在 `vector1` **无匹配标签集**的全部元素。
- `vector1 unless vector2`:结果为 `vector1` 中那些在 `vector2` **不存在**标签集完全匹配元素的元素;两向量中所有匹配元素都被丢弃。
由于这些 logical/set 运算符**不与样本值交互**,**对 float samples 与 histogram samples 行为相同**。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.7 向量匹配
向量间运算为 LHS 每个条目尝试在 RHS 向量中找到匹配元素。有两种基本匹配行为:**One-to-one** 与 **many-to-one / one-to-many**。
#### 2.7.1 匹配关键字
**Vector matching keywords**(允许在不同标签集的序列间匹配):
| 关键字 | 含义 |
|:-------|:-----|
| `on(<label list>)` | 仅在所给标签上匹配 |
| `ignoring(<label list>)` | 匹配时忽略所给标签 |
提供给匹配关键字的标签列表决定向量如何组合。
**Group modifiers**(启用 many-to-one / one-to-many 匹配):
| modifier | 含义 |
|:---------|:-----|
| `group_left` | 允许 many-to-one 匹配,**左侧**向量基数更高 |
| `group_right` | 允许 one-to-many 匹配,**右侧**向量基数更高 |
可为 group modifier 提供标签列表,包含 "one" 侧的标签以纳入结果指标。
> **重要提示**:Many-to-one 与 one-to-many 匹配是**高级用例,应谨慎考虑**;通常正确使用 `ignoring(<labels>)` 即可得到期望结果。Grouping modifiers **只能用于 comparison、arithmetic 与 trigonometric 运算符**;set 运算符默认与任一侧所有可能条目匹配。
#### 2.7.2 One-to-one vector matches
找到运算两侧**唯一的一对**条目。默认情形即 `vector1 <operator> vector2` 形式;两个条目标签集与对应值完全相同则匹配。
- `ignoring` 允许匹配时忽略某些标签;
- `on` 允许将考虑标签集缩减为所给列表。
```text
<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr>
```
**示例输入**:
```text
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
```
**示例查询**:
```promql
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
```
返回结果向量包含各 method 在过去 5 分钟状态码 500 的 HTTP 请求占比。没有 `ignoring(code)` 就不会有匹配,因为两个指标不共享相同标签集。`put` 与 `del` 方法无匹配,不出现在结果中:
```text
{method="get"} 0.04 // 24 / 600
{method="post"} 0.05 // 6 / 120
```
#### 2.7.3 Many-to-one 与 one-to-many vector matches
指 "one" 侧每个向量元素可匹配 "many" 侧多个元素的情形,必须用 `group_left` 或 `group_right` modifiers **显式请求**,left/right 决定哪个向量基数更高:
```text
<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>
```
- group modifier 提供的标签列表包含来自 "one" 侧的额外标签以纳入结果指标。
- 对 `on`,**一个标签只能出现在其中一个列表中**。
- **结果向量的每条时间序列必须唯一可识别。**
**示例查询**:
```promql
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
```
此处左侧向量每个 `method` 标签值有多个条目,用 `group_left` 表示。右侧元素现在与左侧相同 `method` 标签的多个元素匹配:
```text
{method="get", code="500"} 0.04 // 24 / 600
{method="get", code="404"} 0.05 // 30 / 600
{method="post", code="500"} 0.05 // 6 / 120
{method="post", code="404"} 0.175 // 21 / 120
```
#### 2.7.4 on/ignoring 与 group modifier 规则详表
| 组合写法 | 匹配语义 | 结果标签 |
|:---------|:---------|:---------|
| `v1 <op> v2` | one-to-one,全标签集须完全相同 | 两向量标签集交集 |
| `v1 <op> ignoring(<L>) v2` | one-to-one,忽略标签 L 后匹配 | 同上(忽略 L) |
| `v1 <op> on(<L>) v2` | one-to-one,仅按 L 匹配 | 仅保留 L |
| `v1 <op> ignoring(<L>) group_left(<M>) v2` | many-to-one,左多右一 | 左标签 + M(取自 "one" 侧右向量) |
| `v1 <op> ... group_right(<M>) ... v2` | one-to-many,右多左一 | 右标签 + M(取自 "one" 侧左向量) |
| `v1 <op> on(<L>) group_left(<M>) v2` | many-to-one,按 L 匹配 | L + M,L 与 M 不得重叠 |
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.8 聚合运算符
#### 2.8.1 运算符清单
内置聚合运算符用于对**单个 instant vector** 的元素聚合,产生元素更少、值为聚合值的新向量:
| 运算符 | 含义 |
|:-------|:-----|
| `sum(v)` | calculate sum over dimensions |
| `avg(v)` | calculate the arithmetic average over dimensions |
| `min(v)` | select minimum over dimensions |
| `max(v)` | select maximum over dimensions |
| `bottomk(k, v)` | 按样本值取最小的 `k` 个元素 |
| `topk(k, v)` | 按样本值取最大的 `k` 个元素 |
| `limitk(k, v)` | 采样 `k` 个元素,**experimental** |
| `limit_ratio(r, v)` | 采样伪随机比例 `r` 的元素,**experimental** |
| `group(v)` | 结果向量中所有值均为 1 |
| `count(v)` | 向量中元素个数 |
| `count_values(l, v)` | 统计具有相同值的元素个数 |
| `stddev(v)` | 总体标准差(population standard deviation) |
| `stdvar(v)` | 总体方差(population variance) |
| `quantile(φ, v)` | 计算 φ-quantile(0 ≤ φ ≤ 1) |
`limitk` 与 `limit_ratio` 须用 `--enable-feature=promql-experimental-functions` 启用。
#### 2.8.2 by / without 子句
这些运算符既可对**所有**标签维度聚合,也可通过 `without` 或 `by` 子句保留不同维度;子句**可置于表达式之前或之后**:
```text
<aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>)
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
```
- `label list` 为**不带引号**的标签列表,允许尾随逗号,即 `(label1, label2)` 与 `(label1, label2,)` 均为合法语法。
- `without` 从结果向量中**移除所列标签**而保留其他标签;
- `by` 相反,**丢弃未列入 `by` 的标签**,即便这些标签值在向量所有元素间完全相同。
#### 2.8.3 各聚合运算符详解
**`sum(v)`**:以与 `+` 二元运算符相同的方式累加 `v` 中样本值。聚合到同一结果向量元素的所有样本值必须同为 float samples 或 histogram samples;**混合聚合非法**,对应向量元素从输出移除并标记 **warn-level annotation**。
```promql
# 若 memory_consumption_bytes 按 application、instance、group 标签 fanout:
sum without (instance) (memory_consumption_bytes) # 等价于 sum by (application, group) (...)
sum(memory_consumption_bytes) # 所有 application 的总内存消耗
```
**`avg(v)`**:以与 `/` 二元运算符相同的方式用 `v` 之和除以被聚合样本数。同样要求聚合到同一结果元素的所有样本同为 float 或 histogram,混合非法 → 元素移除并标记 warn-level annotation。
**`min(v)` 与 `max(v)`**:分别返回 `v` 中最小/最大值。仅作用于 float samples,遵循 IEEE 754 浮点算术,尤其意味着 **`NaN` 只有在所有被聚合值均为 `NaN` 时才被视为最小/最大值**。输入向量中的 histogram samples 被忽略并标记 **info-level annotation**。
**`topk(k, v)` 与 `bottomk(k, v)`**:与其他聚合器不同,返回输入样本中 `k` 个值的子集(**含原始标签**)到结果向量。`by` 与 `without` 仅用于对输入向量**分桶**。
- 与 `min`/`max` 类似仅作用于 float samples,将 `NaN` 视为距最高/最低最远;输入向量中 histogram samples 被忽略并标记 info-level annotation。
- 若用于 instant query,`topk` 与 `bottomk` 分别按值**降序/升序**返回序列;
- 若配合 `by` 或 `without`,则各桶内序列按值排序且同桶序列连续返回,但**不保证各桶按特定顺序返回**。
- **range queries 不应用排序。**
```promql
topk(5, memory_consumption_bytes) # 跨所有实例内存消耗最高的 5 个实例
```
**`limitk(k, v)`**(experimental):返回 `k` 个输入样本的子集,结果向量含原始标签。子集以**确定性伪随机**方式选取,与样本类型无关,因此对 float samples 与 histogram samples 都适用。
```promql
limitk(10, memory_consumption_bytes)
```
**`limit_ratio(r, v)`**(experimental):返回输入样本的子集,结果向量含原始标签,同样为确定性伪随机选取,与样本类型无关因而适用于两种样本。
- `r` 可取 +1 与 -1 之间;`r` 的绝对值用作选择比例,但**负 `r` 时选择顺序反转**,可用于选取补集。
- `limit_ratio(0.1, ...)` 返回约 10% 输入样本的确定性集合,而 `limit_ratio(-0.9, ...)` 精确返回 `limit_ratio(0.1, ...)` 未返回的其余约 90% 样本。
**`group(v)`**:对每个在该时间戳含任意值的组返回 `1`。值可为 float 或 histogram sample。
**`count(v)`**:返回该时间戳的值的个数,**若该时间戳无值则完全不返回值**。值可为 float 或 histogram sample。
**`count_values(l, v)`**:对 `v` 中每个唯一样本值输出一条时间序列,每条序列带一个由 `l` 给出的附加标签,标签值为该唯一样本值,每条序列的值是该样本值出现的次数。对 float samples 与 histogram samples 都工作;对后者,使用 histogram sample 值的紧凑字符串表示作为标签值。
```promql
count_values("version", build_version) # 统计各构建版本运行的二进制数
```
**`stddev(v)` / `stdvar(v)`**:分别返回 `v` 的标准差 / 方差。仅适用于 float samples,遵循 IEEE 754 浮点算术;输入向量中 histogram samples 被忽略并标记 info-level annotation。
**`quantile(φ, v)`**:计算 φ-quantile,即在所聚合维度的 N 个指标值中排名第 φ*N 的值。仅适用于 float samples;输入向量中 histogram samples 被忽略并标记 info-level annotation。**`NaN` 被视为可能的最小值。**
- `quantile(0.5, ...)` 计算中位数,`quantile(0.95, ...)` 计算 95 分位。
- 特殊情况:φ = `NaN` 时返回 `NaN`;φ < 0 时返回 `-Inf`;φ > 1 时返回 `+Inf`。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.9 二元运算符优先级
二元运算符优先级**从高到低**:
| 优先级 | 运算符 |
|:------:|:-------|
| 1(最高) | `^` |
| 2 | `*`, `/`, `%`, `atan2` |
| 3 | `+`, `-` |
| 4 | `==`, `!=`, `<=`, `<`, `>=`, `>` |
| 5 | `and`, `unless` |
| 6(最低) | `or` |
- 同级运算符**左结合**,例如 `2 * 3 % 2` 等价于 `(2 * 3) % 2`。
- 但 `^` 是**右结合**,故 `2 ^ 3 ^ 2` 等价于 `2 ^ (3 ^ 2)`。
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
### 2.10 实验特性:fill modifiers
**Fill modifiers 为 experimental**,必须用 `--enable-feature=promql-binop-fill-modifiers` 启用。
默认在二元运算另一侧找不到匹配的向量元素不进入结果向量;fill modifiers 允许用所给默认样本值填充任一侧缺失序列:
| modifier | 含义 |
|:---------|:-----|
| `fill(<value>)` | 用 `value` 填充**任一侧**缺失匹配 |
| `fill_left(<value>)` | 用 `value` 填充**左侧**缺失匹配 |
| `fill_right(<value>)` | 用 `value` 填充**右侧**缺失匹配 |
约束:
- `value` 必须是表示 **float sample** 的数值字面量;**不支持 histogram samples**。
- 这些 modifier 只能填充运算某一侧缺失的序列;若序列在两侧都缺失则无法由此创建。
- 允许的组合:`fill(<default>)`;`fill_left(<default>)`;`fill_right(<default>)`;`fill_left(<default>) fill_right(<default>)`;`fill_right(<default>) fill_left(<default>)`。
- 若使用 `bool`、`on`、`ignoring`、`group_left`、`group_right` 等其他二元运算符 modifier,**fill modifiers 必须置于最后**。
与 `group_left`/`group_right` 组合时:
- 若 fill modifier 用于匹配的 "many" 侧,则只为每个匹配组的 "many" 侧填充**单条**序列,以该组的 matching labels 作为序列标识;
- 若用于 "one" 侧且 grouping modifier 指定了要纳入的 "one" 侧标签名(如 `left_vector * on(instance, job) group_left(info_label) fill_right(1) right_vector`),则这些标签不会为缺失序列填充,因为其值无来源。
**Fill modifiers 不支持 set 运算符**(`and`、`or`、`unless`),因为这些运算符的目的就是基于另一向量中的存在与否过滤序列。
**示例查询**(用 `0` 填充任一侧缺失序列):
```promql
method_code:http_errors:rate5m{status="500"} / ignoring(code) fill(0) method:http_requests:rate5m
```
返回各 method 状态码 500 请求占比。`put` 与 `del` 条目因在另一侧无匹配序列,现以填充默认值 `0` 纳入结果:
```text
{method="get"} 0.04 # 24 / 600
{method="put"} +Inf # 3 / 0 (missing right side filled in)
{method="del"} 0 # 0 / 34 (missing left side filled in)
{method="post"} 0.05 # 6 / 120
```
> 引自:[Operators](https://prometheus.io/docs/prometheus/latest/querying/operators/)
---
## 第 3 章 函数全表
> 引自:[Query functions](https://prometheus.io/docs/prometheus/latest/querying/functions/)
本章按功能分类,完整收录官方文档中的全部查询函数(共 **90 个**,含实验性函数)。
### 3.1 通用约定
#### 3.1.1 默认参数
有些函数带默认参数,例如 `year(v=vector(time()) instant-vector)`:当唯一参数 `v` 未提供时,默认取表达式 `vector(time())` 的值。
#### 3.1.2 两类样本语义
- **float samples**:普通浮点样本。
- **histogram samples**:原生直方图(native histogram)样本。
#### 3.1.3 处理 histogram 的三种措辞(最重要的横向规律)
大量函数对 histogram 样本的处理方式只有三种:
| 措辞 | 含义 |
|:-----|:-----|
| **ignored silently** | 静默忽略 |
| **omitted + warn-level annotation** | 整条从结果剔除并给 warn 级注解 |
| **仅用 float 部分 + info-level annotation** | 只取 float 样本并给 info 级注解 |
#### 3.1.4 feature flag
标注 **feature flag** 的函数必须通过 `--enable-feature=promql-experimental-functions` 开启,属实验特性。
### 3.2 缺失检测类
#### 3.2.1 `absent()`
| 项 | 内容 |
|:---|:-----|
| 签名 | `absent(v instant-vector)` |
| 参数 | `v`:instant vector |
| 行为 | 若输入向量有任何元素(float 或 histogram 样本)返回**空向量**;若无任何元素,返回一个只含 `1` 的**单元素向量** |
| 用途 | 对「某 metric + label 组合完全不存在时间序列」的场景做告警 |
它会尝试从输入向量**智能推导**输出标签:
```promql
absent(nonexistent{job="myjob"})
# => {job="myjob"}
absent(nonexistent{job="myjob",instance=~".*"})
# => {job="myjob"}
absent(sum(nonexistent{job="myjob"}))
# => {}
```
#### 3.2.2 `absent_over_time()`
| 项 | 内容 |
|:---|:-----|
| 签名 | `absent_over_time(v range-vector)` |
| 参数 | `v`:range vector |
| 行为 | 语义与 `absent` 相同,但作用在 range-vector 上——区间内有任何元素返回空,无元素返回单元素 `1` |
| 用途 | 对「某组合在**一段时间内**都不存在序列」做告警 |
```promql
absent_over_time(nonexistent{job="myjob"}[1h])
# => {job="myjob"}
absent_over_time(nonexistent{job="myjob",instance=~".*"}[1h])
# => {job="myjob"}
absent_over_time(sum(nonexistent{job="myjob"})[1h:])
# => {}
```
同样会智能推导标签。
### 3.3 取整与数值钳制类
#### 3.3.1 `ceil()`
- **签名**:`ceil(v instant-vector)`
- **行为**:把 float 样本**向上取整**到「≥ 原值的最小整数」;histogram 样本**静默忽略**。
- **特例**:`ceil(+Inf)=+Inf`、`ceil(±0)=±0`、`ceil(1.49)=2.0`、`ceil(1.78)=2.0`。
#### 3.3.2 `floor()`
- **签名**:`floor(v instant-vector)`
- **行为**:**向下取整**到「≤ 原值的最大整数」;histogram 静默忽略。
- **特例**:`floor(+Inf)=+Inf`、`floor(±0)=±0`、`floor(1.49)=1.0`、`floor(1.78)=1.0`。
#### 3.3.3 `round()`
- **签名**:`round(v instant-vector, to_nearest=1 scalar)`
- **参数**:`to_nearest`(可选,默认 `1`):指定「就近的倍数」,可以是小数(分数)。
- **行为**:把所有样本**四舍五入**到最近整数,**平局向上取整**;histogram 静默忽略。
#### 3.3.4 `clamp()`
- **签名**:`clamp(v instant-vector, min scalar, max scalar)`
- **行为**:把 `v` 中 float 样本**钳制**到 `[min, max]`;histogram 静默忽略。
- **特例**:
- `min > max` 时返回**空向量**;
- `min` 或 `max` 为 `NaN` 时返回 `NaN`;
- `min=-Inf` 且 `max=+Inf` 时样本不变。
#### 3.3.5 `clamp_max()`
- **签名**:`clamp_max(v instant-vector, max scalar)`
- **行为**:只设上限 `max`;histogram 静默忽略。
- **特例**:`max=NaN` 返回 `NaN`;`max=+Inf` 不变;`max=-Inf` 时所有 float 样本置为 `-Inf`。
#### 3.3.6 `clamp_min()`
- **签名**:`clamp_min(v instant-vector, min scalar)`
- **行为**:只设下限 `min`;histogram 静默忽略。
- **特例**:`min=NaN` 返回 `NaN`;`min=-Inf` 不变;`min=+Inf` 时所有 float 样本置为 `+Inf`。
### 3.4 数学函数
| 函数 | 签名 | 行为 | 特例 |
|:-----|:-----|:-----|:-----|
| `exp` | `exp(v instant-vector)` | 求所有 float 样本的指数函数;histogram 静默忽略 | `Exp(+Inf)=+Inf`、`Exp(NaN)=NaN` |
| `ln` | `ln(v instant-vector)` | 自然对数;histogram 静默忽略 | `ln(+Inf)=+Inf`、`ln(0)=-Inf`、`ln(x<0)=NaN`、`ln(NaN)=NaN` |
| `log2` | `log2(v instant-vector)` | 二进制对数;histogram 静默忽略 | 与 `ln` 等价 |
| `log10` | `log10(v instant-vector)` | 十进制对数;histogram 静默忽略 | 与 `ln` 等价 |
| `sqrt` | `sqrt(v instant-vector)` | 平方根;histogram 静默忽略 | — |
| `sgn` | `sgn(v instant-vector)` | 取符号:正数→`1`,负数→`-1`,零→`0`;histogram 静默忽略 | — |
| `abs` | `abs(v instant-vector)` | 取绝对值;**输入中的 histogram 样本被静默忽略**(明确写在 `abs` 描述里) | — |
### 3.5 时间与日期类
以下函数把 float 样本**解释为时间戳**(自 1970-01-01 UTC 起的秒数),全部以 **UTC** 计算,且都**静默忽略** histogram 样本。
#### 3.5.1 常用日期时间函数
| 函数 | 签名 | 返回值范围 |
|:-----|:-----|:-----------|
| `day_of_month` | `day_of_month(v=vector(time()) instant-vector)` | 当月第几天,1–31 |
| `day_of_week` | `day_of_week(v=vector(time()) instant-vector)` | 星期几,0–6,**0 表示周日** |
| `day_of_year` | `day_of_year(v=vector(time()) instant-vector)` | 当年第几天,平年 1–365、闰年 1–366 |
| `days_in_month` | `days_in_month(v=vector(time()) instant-vector)` | 当月天数,28–31 |
| `hour` | `hour(v=vector(time()) instant-vector)` | 小时,0–23 |
| `minute` | `minute(v=vector(time()) instant-vector)` | 分钟,0–59 |
| `month` | `month(v=vector(time()) instant-vector)` | 月份,1–12,**1 表示一月** |
| `year` | `year(v=vector(time()) instant-vector)` | 年份(UTC) |
#### 3.5.2 当前时间与样本时间戳
**`time()`**
- 返回自 1970-01-01 UTC 起的秒数。
- **关键注意事项**:返回的**不是真实当前时间**,而是该表达式被求值的时刻。
**`timestamp(v instant-vector)`**
- 返回给定向量中每个样本的时间戳(秒,UTC);对 float 与 histogram 样本行为一致。
**`start_timestamp(v instant-vector)`**
- 返回给定向量每个样本的**起始时间戳**(秒,UTC),对 float 与 histogram 行为一致。
- **限制**:只有**直接作用于 instant vector** 且启用 `use-start-timestamps` feature flag 时才有效;若作用于表达式或该 flag 未开启,**返回空结果**。
#### 3.5.3 实验性:查询区间信息与标量比较
以下函数均需 feature flag `--enable-feature=promql-experimental-functions`:
| 函数 | 签名 | 行为 |
|:-----|:-----|:-----|
| `start` | `start()` | 返回当前查询区间求值的起始时间戳(秒,UTC)。instant query 时等于求值时间戳 |
| `end` | `end()` | 返回当前查询区间求值的结束时间戳(秒,UTC)。instant query 时等于求值时间戳 |
| `step` | `step()` | 返回查询分辨率步长(秒)。instant query 返回 `0` |
| `range` | `range()` | 返回当前查询区间时长(秒),等价于 `end() - start()`;instant query 返回 `0` |
| `max_of` | `max_of(a scalar, b scalar)` | 返回两个标量中较大者 |
| `min_of` | `min_of(a scalar, b scalar)` | 返回两个标量中较小者 |
### 3.6 范围向量(趋势/变化/速率)类
#### 3.6.1 函数总览
| 函数 | 签名 | 适用类型 | 样本支持 |
|:-----|:-----|:---------|:---------|
| `delta` | `delta(v range-vector)` | **gauge**(float 与 histogram 皆然) | float + histogram |
| `deriv` | `deriv(v range-vector)` | gauge | 仅 float |
| `predict_linear` | `predict_linear(v range-vector, t scalar)` | gauge | 仅 float |
| `double_exponential_smoothing` | `double_exponential_smoothing(v range-vector, sf scalar, tf scalar)` | gauge(**实验性**) | 仅 float |
| `rate` | `rate(v range-vector)` | **counter**(float 与 histogram) | float + histogram |
| `irate` | `irate(v range-vector)` | counter | float + histogram |
| `increase` | `increase(v range-vector)` | counter | float + histogram |
| `changes` | `changes(v range-vector)` | 通用 | float + histogram |
| `resets` | `resets(v range-vector)` | counter | float + histogram |
| `idelta` | `idelta(v range-vector)` | gauge(float 与 histogram 皆然) | float + histogram |
#### 3.6.2 `delta()`
- 计算区间内**首末样本值之差**,返回带等价标签的 instant vector。
- 差值会**外推到整个区间**,因此即使样本值全为整数也可能得到非整数结果。
- 示例:`delta(cpu_temp_celsius{host="zeus"}[2h])` 返回「现在与 2 小时前」的 CPU 温度差。
- 对 histogram 的处理:计算一个新的 histogram,其每个分量(观测 sum、count、buckets)为首末原生直方图对应分量之差;**区间内混有 float 与 histogram 样本的元素会被剔除并打 warn 级注解**。
- **仅应用于 gauge(float 与 histogram 皆然)。**
#### 3.6.3 `deriv()`
- 用**简单线性回归**计算每个 float 时间序列的**每秒导数**。
- 区间至少需 **2 个 float 样本**;区间内出现 `+Inf`/`-Inf` 时斜率与截距都为 `NaN`。
- **仅用于 gauge,且只支持 float**:只含 histogram 的元素被**完全忽略**;混含的元素只取 float 输入并打 **info 级注解**。
#### 3.6.4 `predict_linear()`
- 基于区间 `v` 用简单线性回归预测**从此刻起 `t` 秒后**的值。
- 区间至少需 2 个 float 样本;含 `±Inf` 时预测值为 `NaN`。
- **仅用于 gauge,只支持 float**;处理规则同 `deriv`(纯 histogram 元素忽略,混含只取 float 并打 info 注解)。
#### 3.6.5 `double_exponential_smoothing()`(实验性)
- **签名**:`double_exponential_smoothing(v range-vector, sf scalar, tf scalar)`
- 对区间内每个 float 时间序列产生**平滑值**。
- `sf`(平滑因子)越小,越看重**旧**数据;`tf`(趋势因子)越大,越多考虑**趋势**。`sf` 与 `tf` 都必须在 **0–1** 之间。
- **Prometheus V2 中此函数名为 `holt_winters`**,官方说明改名原因:Holt-Winters 通常指三次指数平滑,而此处实现的是二次("Holt Linear"),原名易致混淆。
- **仅用于 gauge,只支持 float**;纯 histogram 元素完全忽略,混含只取 float 并打 info 注解。
#### 3.6.6 `rate()`
- 计算区间内时间序列的**每秒平均增长率**。
- 自动处理单调性中断(如 target 重启导致的 counter reset);并**外推到区间两端**,以容忍漏抓取或抓取周期与区间未对齐。
- 示例:`rate(http_requests_total{job="api-server"}[5m])`。
- 对原生直方图:生成新直方图,各分量(sum、count、buckets)为首末原生直方图对应分量的增长率;区间内混含 float 与原生直方图样本的元素被剔除并打 warn 注解。
- **仅用于 counter(float 与 histogram 皆然)**,最适合**告警**与**缓慢变化 counter 的绘图**。
- **重要**:与聚合算子(如 `sum()`)或任何以 `_over_time` 结尾的聚合函数组合时,**必须先用 `rate()` 再聚合**,否则 target 重启时无法检测 counter reset。
#### 3.6.7 `irate()`
- 基于**最后两个数据点**计算每秒**瞬时**增长率,自动处理单调性中断。
- 最后两个样本必须**同为 float 或同为 histogram**,否则该元素被剔除并打 warn 注解。
- **仅用于 counter(float 与 histogram)**。示例:`irate(http_requests_total{job="api-server"}[5m])`。
- **仅建议用于绘图「易变、快速变化」的 counter**;告警与缓慢 counter 用 `rate`,因为速率短暂变化会重置 `FOR` 子句,且全为孤立尖峰的图难以阅读。
- **同样**:与聚合算子或 `_over_time` 组合时**必须先 `irate()` 后聚合**,否则无法在 target 重启时识别 counter reset。
#### 3.6.8 `increase()`
- 计算区间内时间序列的**增量**,自动修正单调性中断,并**外推到整个区间**(因此即使 counter 仅按整数增长也可能得到非整数结果)。
- 示例:`increase(http_requests_total{job="api-server"}[5m])`。
- 对 histogram:生成新直方图,各分量为首末原生直方图对应分量的增量;混含 float 与 histogram 的元素被剔除并打 warn 注解。
- **仅用于 counter(float 与 histogram)**。
- 它本质是 `rate(v)` 乘以区间秒数的**语法糖**,主要用于人类可读性;**在 recording rules 中应使用 `rate`**,以便按秒一致地追踪增量。
#### 3.6.9 `changes()`
- 对每个输入时间序列,返回区间内**值发生变化的次数**(instant vector)。
- float 样本后跟 histogram 样本(或反之)**计作一次变化**;counter histogram 后跟一个数值完全相同的 gauge histogram(或反之)**不计**为变化。
#### 3.6.10 `resets()`
- 对每个序列返回区间内 **counter reset 次数**。
- float 上任何相邻两点间的下降都算一次 reset。
- 原生直方图的 reset 判定更复杂:任一 bucket(含 zero bucket)或观测计数的下降算 reset;**此前有值的 bucket 消失、zero-bucket 宽度减小、以及任何非「兼容的降分辨率」的 schema 变更**也都算 reset。
- **仅用于 counter(float 与 histogram)**。
- float 与 histogram 相邻互计为 reset;counter histogram 与 gauge histogram 相邻也计为 reset(但注意 resets 本就不该用于 gauge)。
#### 3.6.11 `idelta()`
- 计算区间 `v` 中**最后两个样本**之差,返回 instant vector。
- 两个样本必须同为 float 或同为 histogram;若最后两个一为 float 一为 histogram,则该元素被剔除并打 warn 注解。
- **仅用于 gauge(float 与 histogram 皆然)。**
### 3.7 直方图专用函数
| 函数 | 签名 | 说明 |
|:-----|:-----|:-----|
| `histogram_avg` | `histogram_avg(v instant-vector)` | 原生直方图观测值的算术平均 |
| `histogram_count` | `histogram_count(v instant-vector)` | 原生直方图储存的观测计数 |
| `histogram_sum` | `histogram_sum(v instant-vector)` | 原生直方图观测值之和 |
| `histogram_fraction` | `histogram_fraction(lower scalar, upper scalar, b instant-vector)` | 区间内观测的估算占比 |
| `histogram_quantile` | `histogram_quantile(φ scalar, b instant-vector)` | φ-分位数 |
| `histogram_quantiles` | `histogram_quantiles(v instant-vector, quantile_label string, φ_1, φ_2, ...)` | 一次算多个分位数(**实验性**) |
| `histogram_stddev` | `histogram_stddev(v instant-vector)` | 估计标准差 |
| `histogram_stdvar` | `histogram_stdvar(v instant-vector)` | 估计方差 |
#### 3.7.1 `histogram_avg()`
返回 `v` 中每个原生直方图样本观测值的**算术平均**;float 样本被忽略、不出现在结果中。
```promql
histogram_avg(rate(http_request_duration_seconds[5m]))
```
等价于 `histogram_sum(rate(...)) / histogram_count(rate(...))`。
#### 3.7.2 `histogram_count()` 与 `histogram_sum()`
- `histogram_count(v instant-vector)`:返回每个原生直方图样本中储存的**观测计数**;float 样本被忽略。
- `histogram_sum(v instant-vector)`:返回每个原生直方图样本中**观测值之和**;float 样本被忽略。
```promql
histogram_count(rate(http_request_duration_seconds[10m])) # 每秒请求数
```
#### 3.7.3 `histogram_fraction()`
- 返回 `b` 中每个经典或原生直方图在 `[lower, upper]` 之间观测值的**估算占比**。
- `b` 中的 float 样本被视为「一个或多个经典直方图各 bucket 的计数」,而原生直方图样本各自独立当作一个直方图处理,逻辑与 `histogram_quantile()` 相同。
- 若给定边界**不与 bucket 边界重合**,结果为**估算值**,使用与 `histogram_quantile()` 相同的插值法;**经典直方图下极易选到远离任何 bucket 边界的上下界,导致误差很大**,此时官方建议直接对 bucket 序列运算(参考 Apdex 计算)。
- 示例:`histogram_fraction(0, 0.2, rate(http_request_duration_seconds[1h]))` 表示过去一小时耗时 ≤200ms 的请求占比。
- `+Inf`/`-Inf` 是**合法边界**(若要含所有 ≤0.2 的观测含负值,下界应用 `-Inf` 而非 `0`)。
- 边界开闭仅在**恰好对齐 bucket 边界**时才有意义,此时行为取决于直方图 schema 定义(标准指数 schema 对正值是「上界含、下界不含」,负值相反);未对齐时用插值,开闭无关紧要。
- 原生标准指数桶特例:`NaN` 观测被视为落在所有 bucket 之外,`histogram_fraction(-Inf, +Inf, b)` 实际返回非 `NaN` 观测占比,可能小于 1。
#### 3.7.4 `histogram_quantile()`
从经典直方图或原生直方图计算 φ-分位数(0 ≤ φ ≤ 1)。
**输入解释**:
- `b` 的 float 样本被视为「一个或多个经典直方图各 bucket 的计数」,**每个 float 样本必须带 `le` 标签**表示该 bucket 的**含上界**;**不带此标签的 float 样本被静默忽略**;其余标签与 metric 名用于识别各 bucket 归属哪个经典直方图(histogram 类型自动生成带 `_bucket` 后缀的时间序列)。
- `b` 中的原生直方图样本各自独立计算分位数。
- 只要不产生命名冲突,`b` 可**混含**经典与原生直方图。
- 用 `rate()` 指定时间窗口。
**示例**:
```promql
# 经典直方图
histogram_quantile(0.9, rate(http_request_duration_seconds_bucket[10m]))
# 原生直方图
histogram_quantile(0.9, rate(http_request_duration_seconds[10m]))
# 聚合(经典,需把 le 放进 by)
histogram_quantile(0.9, sum by (job, le) (rate(http_request_duration_seconds_bucket[10m])))
# 聚合(原生,简化为 sum by (job))
histogram_quantile(0.9, sum by (job) (rate(http_request_duration_seconds[10m])))
# 经典全聚合需只保留 le
histogram_quantile(0.9, sum by (le) (rate(http_request_duration_seconds_bucket[10m])))
# 原生全聚合无需 by
histogram_quantile(0.9, sum(rate(http_request_duration_seconds[10m])))
```
**插值方式**:
- 经典直方图、原生自定义边界直方图、以及原生的 zero bucket 使用**线性插值**(假设桶内均匀分布);
- 原生标准指数 schema 的非零桶使用**指数插值**(假设样本在更高分辨率的假设直方图中均匀填充各桶)。
**返回值特例**:
| 条件 | 返回 |
|:-----|:-----|
| `b` 有 0 个观测 | `NaN` |
| `φ < 0` | `-Inf` |
| `φ > 1` | `+Inf` |
| `φ = NaN` | `NaN` |
**经典直方图特例**:
- bucket 少于 2 个返回 `NaN`;
- 最高 bucket 上界必须是 `+Inf`(否则返回 `NaN`);
- 分位数落在最高桶时返回**次高桶上界**;
- 最低桶上界 > 0 时假定其下界为 0 并做线性插值,否则(上界 ≤ 0)落在最低桶的分位数直接返回该桶上界。
**原生直方图特例**:
- 标准指数桶含 `NaN` 观测且分位数落在已有指数桶内时,因 `NaN` 被当作 `+Inf`,结果会**偏高**,打 **info 注解**;
- 若分位数高于所有已有指数桶则返回 `NaN`,也打 info 注解;
- 有限宽 zero bucket 在「有正桶观测但无负桶观测」时被假定不含负观测,反之亦然。
**极值估计**:
- `histogram_quantile(0, v)` 可得估计最小值;
- `histogram_quantile(1, v)` 可得估计最大值。
**累积性假设与修正**(经典直方图):
- bucket 计数应**单调不减**,相邻两桶上界之间无观测时两桶计数应相等。
- 浮点精度问题(如 `sum(rate(...))` 引入的小偏差)或非法数据会违反该假设导致结果无意义。
- 缓解措施:把相邻桶的极小相对差异(阈值为两桶之和的**万亿分之一,1e-12**)视为浮点误差而忽略;若调整后仍非单调,则把计数抬升到前一桶的值以强制单调。
- **此情形会打 info 级注解** `input to histogram_quantile needed to be fixed for monotonicity`,遇到该注解应找出并移除非法数据源。
#### 3.7.5 `histogram_quantiles()`(实验性)
- **签名**:`histogram_quantiles(v instant-vector, quantile_label string, φ_1 scalar, φ_2 scalar, ...)`
- 从经典或原生直方图**一次性计算多个(1 到 10 个)** φ-分位数,计算方式与 `histogram_quantile()` 相同。
- 第二个字符串参数指定用于在结果中区分不同分位数的**标签名**。
```promql
histogram_quantiles(sum(rate(foo[1m])), "quantile", 0.9, 0.99)
# => {quantile="0.9"} 123
# {quantile="0.99"} 128
```
#### 3.7.6 `histogram_stddev()` 与 `histogram_stdvar()`
- `histogram_stddev(v instant-vector)`:返回每个原生直方图样本观测值的**估计标准差**。估计时假定每个桶内所有观测都取该桶边界的**均值**:zero bucket 与自定义边界桶用**算术平均**,标准指数桶用**几何平均**。float 样本被忽略。
- `histogram_stdvar(v instant-vector)`:返回每个原生直方图样本观测值的**估计方差**,其余同 `histogram_stddev`。
### 3.8 标签操作类
#### 3.8.1 `label_join()`
- **签名**:`label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...)`
- 对 `v` 中每个时间序列,用 `separator` 拼接所有 `src_labels` 的值,返回把这些值写入 `dst_label` 后的序列。`src_labels` 数量不限。
- 对 float 与 histogram 样本行为一致。
```promql
label_join(up{job="api-server",src1="a",src2="b",src3="c"}, "foo", ",", "src1", "src2", "src3")
# 给每条序列加上 foo="a,b,c"
```
#### 3.8.2 `label_replace()`
- **签名**:`label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)`
- 用 `regex` 匹配 `src_label` 的值,匹配成功则把 `dst_label` 设为 `replacement` 的展开结果(保留原有标签);反向引用可写 `$1`、`$2`…,**命名捕获组可写 `$name`**;正则不匹配则序列原样返回。
- 对 float 与 histogram 行为一致。
```promql
label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")
# 返回 service="a:c"、foo="a"
# 等价写法:命名组
label_replace(up{job="api-server",service="a:c"}, "foo", "$name", "service", "(?P<name>.*):(?P<version>.*)")
```
#### 3.8.3 `info()`(实验性)
> **文档明确警告**:未来版本行为可能变化,甚至从 PromQL 移除。须 `--enable-feature=promql-experimental-functions` 启用。
- **签名**:`info(v instant-vector, [data-label-selector instant-vector])`
- 为 `v` 中每条序列找到所有「identifying 标签匹配」的 info 序列,并把它们的 **data(非 identifying)标签**做并集加到该序列上。
- 第二参数可选,**不是真正的 instant vector,只用其语法子集**,必须以 `{ ... }` 起止且只能包含标签匹配器,用于约束「考虑哪些 info 序列」与「加入哪些 data 标签」。
- 若某时刻找不到匹配 info 序列(如 info 序列已 stale):
- 若 `data-label-selector` 含任何**不匹配空串**的匹配器(如 `{data=~".+"}`),该序列在该时间戳被**丢弃**(所需增强不可得);
- 若所有匹配器都匹配空串(如 `{data=~".*"}`)或未提供第二参数,则序列在**无增强**的情况下被返回。
**概念说明**:info 序列的 identifying 标签是唯一标识该序列的标签子集,其余为 data 标签。Prometheus 的时间序列身份概念始终包含**全部**标签,而 `info` 函数在「逻辑上」另定义了一套 info 序列身份。identifying 标签用于与普通(非 info)序列 join;data 标签承载元数据键值对。这意味着传统视角下 data 标签变化 = 一条 info 序列结束、另一条开始,而 `info` 的逻辑视角视为同一 info 序列延续、只是 data 不同。
**对比传统 join 写法**:
```promql
rate(http_server_request_duration_seconds_count[2m])
* on (job, instance) group_left (k8s_cluster_name)
target_info
```
冗长且不直观,还可能有「身份危机」——若旧 `target_info` 未正确标记为 stale,会在最多 5m(lookback delta)内报错,因为同时匹配到新旧两版。
用 `info` 简化:
```promql
info(rate(http_server_request_duration_seconds_count[2m]), {k8s_cluster_name=~".+"})
```
省略第二参数即加**全部** data 标签:
```promql
info(rate(http_server_request_duration_seconds_count[2m]))
```
可用 `__name__` 匹配器限定 info 序列,如 `{__name__="target_info"}`。
**注意**:若 `v` 中某些序列本身匹配 `data-label-selector`(或未指定时的默认 `target_info`),它们会被当作 info 序列并**原样返回**。
**Limitations**:
- 当前版本 `info` 默认只考虑名为 `target_info` 的 info 序列,并假定 identifying 标签为 `instance` 与 `job`。
- 通过 `__name__` 匹配器可支持其他 info 序列名,如 `{__name__=~"(target|build)_info"}` 同时考虑两者,但 **identifying 标签必须始终是 `instance` 和 `job`**。
- 当只提供**否定式** `__name__` 匹配器(如 `{__name__!="target_info"}`)时,`info` 会先考虑所有匹配 `.+_info` 的 metric 再做否定过滤,因为单靠否定匹配器无法正向确定要纳入哪些 info metric。
- identifying 标签的存在性是**按输入序列逐条判定**的:只含 `job`、只含 `instance`、或两者都有的输入都能从对应 info 序列组获得 data 标签;**缺失的 identifying 标签不会被当作通配符**。
- 文档承认这些限制部分削弱了 `info` 的初衷,当前仍是实验,最终版本会考虑所有匹配的 info 序列及其恰当的 identifying 标签。
### 3.9 排序与向量构造类
#### 3.9.1 `sort()` 与 `sort_desc()`
- `sort(v instant-vector)`:按 float 样本值**升序**排序;histogram 静默忽略。
- `sort_desc(v instant-vector)`:同 `sort`,但**降序**。
- **注意**:`sort` 只影响 **instant query** 的结果,**range query 结果始终有固定输出顺序**。
#### 3.9.2 `sort_by_label()` 与 `sort_by_label_desc()`(实验性)
- `sort_by_label(v instant-vector, label string, ...)`:按给定标签值**升序**排序;标签值相等时按完整标签集排序。对 float 与 histogram 行为一致。
- `sort_by_label_desc(v instant-vector, label string, ...)`:同 `sort_by_label`,但**降序**。
- 只影响 instant query 结果(同 `sort`)。**使用自然排序(natural sort order)。**
#### 3.9.3 `scalar()` 与 `vector()`
- `scalar(v instant-vector)`:若输入向量**恰好只有一个** float 样本元素,返回其样本值作为 scalar;否则返回 `NaN`。histogram 样本被静默忽略。
- `vector(s scalar)`:把标量 `s` 转为一个 float 样本,返回**无标签的单元素** instant vector。
### 3.10 over_time 聚合系列
对给定 range vector 的每条序列做**区间聚合**,返回带「逐序列聚合结果」的 instant vector。
#### 3.10.1 标准函数
| 函数 | 签名 | 说明 |
|:-----|:-----|:-----|
| `avg_over_time` | `avg_over_time(range-vector)` | 区间内所有 float 或 histogram 样本的平均值 |
| `min_over_time` | `min_over_time(range-vector)` | 区间内所有 float 样本的最小值 |
| `max_over_time` | `max_over_time(range-vector)` | 区间内所有 float 样本的最大值 |
| `sum_over_time` | `sum_over_time(range-vector)` | 区间内所有 float 或 histogram 样本之和 |
| `count_over_time` | `count_over_time(range-vector)` | 区间内所有样本的计数 |
| `quantile_over_time` | `quantile_over_time(scalar, range-vector)` | 区间内所有 float 样本的 φ-分位数(0 ≤ φ ≤ 1) |
| `stddev_over_time` | `stddev_over_time(range-vector)` | 区间内所有 float 样本的**总体**标准差 |
| `stdvar_over_time` | `stdvar_over_time(range-vector)` | 区间内所有 float 样本的**总体**方差 |
| `last_over_time` | `last_over_time(range-vector)` | 区间内**最新**样本 |
| `first_over_time` | `first_over_time(range-vector)` | 区间内**最旧**样本 |
| `present_over_time` | `present_over_time(range-vector)` | 区间内任何序列都返回值 `1` |
#### 3.10.2 实验性附加函数(需 feature flag)
| 函数 | 签名 | 说明 |
|:-----|:-----|:-----|
| `mad_over_time` | `mad_over_time(range-vector)` | 区间内所有 float 样本的**中位数绝对偏差** |
| `ts_of_min_over_time` | `ts_of_min_over_time(range-vector)` | 区间内取到最小值的那(最后)个 float 样本的时间戳 |
| `ts_of_max_over_time` | `ts_of_max_over_time(range-vector)` | 区间内取到最大值的那(最后)个 float 样本的时间戳 |
| `ts_of_last_over_time` | `ts_of_last_over_time(range-vector)` | 区间内最后一个样本的时间戳 |
| `ts_of_first_over_time` | `ts_of_first_over_time(range-vector)` | 区间内最早一个样本的时间戳 |
#### 3.10.3 权重与 histogram 处理
**权重说明**:区间内所有值在聚合中**权重相同**,即使它们在区间内分布不均。
**对 histogram 的处理**:
- `count_over_time`、`first_over_time`、`last_over_time`、`present_over_time()` 对 float 与 histogram 样本行为一致。
- `avg_over_time()` 与 `sum_over_time()` 对 histogram 样本的处理等同于对应的聚合算子;若某序列区间内混含 float 与 histogram 样本,该结果会被**整体移除**并打 **warn 级注解**。
- 其余函数忽略 histogram 的方式为:只含 histogram 的区间被**静默移除**;混含 float 与 histogram 的区间只处理 float 样本,忽略 histogram 的部分打 **info 级注解**。
**补充对比**:`first_over_time(m[1m])` 与 `m offset 1m` 不同——前者选取 1m 区间**之内**的第一个样本,而 `m offset 1m` 选取的是 lookback 区间内、位于 1m 偏移**之外且之前**的最近样本。这在 range query 中用 `first_over_time(m[step()])` 可确保所选样本落在区间步长之内。
### 3.11 三角函数
三角函数以**弧度**为单位,且**忽略输入向量中的 histogram 样本**。
#### 3.11.1 反三角与双曲函数
| 函数 | 签名 | 对应 Go |
|:-----|:-----|:--------|
| `acos` | `acos(v instant-vector)` | 反余弦(`math#Acos`) |
| `acosh` | `acosh(v instant-vector)` | 反双曲余弦(`math#Acosh`) |
| `asin` | `asin(v instant-vector)` | 反正弦(`math#Asin`) |
| `asinh` | `asinh(v instant-vector)` | 反双曲正弦(`math#Asinh`) |
| `atan` | `atan(v instant-vector)` | 反正切(`math#Atan`) |
| `atanh` | `atanh(v instant-vector)` | 反双曲正切(`math#Atanh`) |
| `cos` | `cos(v instant-vector)` | 余弦(`math#Cos`) |
| `cosh` | `cosh(v instant-vector)` | 双曲余弦(`math#Cosh`) |
| `sin` | `sin(v instant-vector)` | 正弦(`math#Sin`) |
| `sinh` | `sinh(v instant-vector)` | 双曲正弦(`math#Sinh`) |
| `tan` | `tan(v instant-vector)` | 正切(`math#Tan`) |
| `tanh` | `tanh(v instant-vector)` | 双曲正切(`math#Tanh`) |
#### 3.11.2 角度/弧度转换
| 函数 | 签名 | 说明 |
|:-----|:-----|:-----|
| `deg` | `deg(v instant-vector)` | 弧度 → 角度 |
| `pi` | `pi()` | 返回 pi |
| `rad` | `rad(v instant-vector)` | 角度 → 弧度 |
### 3.12 函数速查总表
下表按字母序汇总全部 90 个函数及其分类与实验性标记。
| # | 函数 | 分类 | 实验性 | 备注 |
|:--:|:-----|:-----|:------:|:-----|
| 1 | `abs` | 数学 | | histogram 静默忽略 |
| 2 | `absent` | 缺失检测 | | |
| 3 | `absent_over_time` | 缺失检测 | | |
| 4 | `acos` | 三角函数 | | |
| 5 | `acosh` | 三角函数 | | |
| 6 | `asin` | 三角函数 | | |
| 7 | `asinh` | 三角函数 | | |
| 8 | `atan` | 三角函数 | | |
| 9 | `atanh` | 三角函数 | | |
| 10 | `avg_over_time` | over_time | | 支持 histogram |
| 11 | `ceil` | 取整/钳制 | | |
| 12 | `changes` | 范围向量 | | 支持 histogram |
| 13 | `clamp` | 取整/钳制 | | |
| 14 | `clamp_max` | 取整/钳制 | | |
| 15 | `clamp_min` | 取整/钳制 | | |
| 16 | `cos` | 三角函数 | | |
| 17 | `cosh` | 三角函数 | | |
| 18 | `count_over_time` | over_time | | 支持 histogram |
| 19 | `day_of_month` | 时间/日期 | | |
| 20 | `day_of_week` | 时间/日期 | | 0 = 周日 |
| 21 | `day_of_year` | 时间/日期 | | |
| 22 | `days_in_month` | 时间/日期 | | |
| 23 | `deg` | 三角函数 | | |
| 24 | `delta` | 范围向量 | | gauge;支持 histogram |
| 25 | `deriv` | 范围向量 | | gauge;仅 float |
| 26 | `double_exponential_smoothing` | 范围向量 | ✅ | 原 `holt_winters` |
| 27 | `end` | 时间/日期 | ✅ | |
| 28 | `exp` | 数学 | | |
| 29 | `first_over_time` | over_time | | 支持 histogram |
| 30 | `floor` | 取整/钳制 | | |
| 31 | `histogram_avg` | 直方图 | | |
| 32 | `histogram_count` | 直方图 | | |
| 33 | `histogram_fraction` | 直方图 | | |
| 34 | `histogram_quantile` | 直方图 | | |
| 35 | `histogram_quantiles` | 直方图 | ✅ | |
| 36 | `histogram_stddev` | 直方图 | | |
| 37 | `histogram_stdvar` | 直方图 | | |
| 38 | `histogram_sum` | 直方图 | | |
| 39 | `hour` | 时间/日期 | | |
| 40 | `idelta` | 范围向量 | | gauge;支持 histogram |
| 41 | `increase` | 范围向量 | | counter;支持 histogram |
| 42 | `info` | 标签操作 | ✅ | 可能被移除 |
| 43 | `irate` | 范围向量 | | counter;支持 histogram |
| 44 | `label_join` | 标签操作 | | |
| 45 | `label_replace` | 标签操作 | | |
| 46 | `last_over_time` | over_time | | 支持 histogram |
| 47 | `ln` | 数学 | | |
| 48 | `log10` | 数学 | | |
| 49 | `log2` | 数学 | | |
| 50 | `mad_over_time` | over_time | ✅ | |
| 51 | `max_of` | 时间/日期 | ✅ | |
| 52 | `max_over_time` | over_time | | 仅 float |
| 53 | `min_of` | 时间/日期 | ✅ | |
| 54 | `min_over_time` | over_time | | 仅 float |
| 55 | `minute` | 时间/日期 | | |
| 56 | `month` | 时间/日期 | | 1 = 一月 |
| 57 | `pi` | 三角函数 | | |
| 58 | `predict_linear` | 范围向量 | | gauge;仅 float |
| 59 | `present_over_time` | over_time | | 支持 histogram |
| 60 | `quantile_over_time` | over_time | | 仅 float |
| 61 | `rad` | 三角函数 | | |
| 62 | `range` | 时间/日期 | ✅ | |
| 63 | `rate` | 范围向量 | | counter;支持 histogram |
| 64 | `resets` | 范围向量 | | counter;支持 histogram |
| 65 | `round` | 取整/钳制 | | |
| 66 | `scalar` | 排序/构造 | | |
| 67 | `sgn` | 数学 | | |
| 68 | `sin` | 三角函数 | | |
| 69 | `sinh` | 三角函数 | | |
| 70 | `sort` | 排序/构造 | | |
| 71 | `sort_by_label` | 排序/构造 | ✅ | |
| 72 | `sort_by_label_desc` | 排序/构造 | ✅ | |
| 73 | `sort_desc` | 排序/构造 | | |
| 74 | `sqrt` | 数学 | | |
| 75 | `start` | 时间/日期 | ✅ | |
| 76 | `start_timestamp` | 时间/日期 | | 需 `use-start-timestamps` |
| 77 | `stddev_over_time` | over_time | | 仅 float |
| 78 | `stdvar_over_time` | over_time | | 仅 float |
| 79 | `step` | 时间/日期 | ✅ | |
| 80 | `sum_over_time` | over_time | | 支持 histogram |
| 81 | `tan` | 三角函数 | | |
| 82 | `tanh` | 三角函数 | | |
| 83 | `time` | 时间/日期 | | |
| 84 | `timestamp` | 时间/日期 | | 支持 histogram |
| 85 | `ts_of_first_over_time` | over_time | ✅ | |
| 86 | `ts_of_last_over_time` | over_time | ✅ | |
| 87 | `ts_of_max_over_time` | over_time | ✅ | |
| 88 | `ts_of_min_over_time` | over_time | ✅ | |
| 89 | `vector` | 排序/构造 | | |
| 90 | `year` | 时间/日期 | | |
> 统计:共 **90** 个函数。其中需 `--enable-feature=promql-experimental-functions` 开启的**实验性函数共 16 个**:`double_exponential_smoothing`、`end`、`histogram_quantiles`、`info`、`mad_over_time`、`max_of`、`min_of`、`range`、`sort_by_label`、`sort_by_label_desc`、`start`、`step`、`ts_of_first_over_time`、`ts_of_last_over_time`、`ts_of_max_over_time`、`ts_of_min_over_time`。另有 `start_timestamp` 依赖 `use-start-timestamps` 特性开关,`info`/`histogram_quantiles` 的未来版本可能发生不兼容变化。
> 引自:[Query functions](https://prometheus.io/docs/prometheus/latest/querying/functions/)
---
## 第 4 章 查询示例集
> 引自:[Query examples](https://prometheus.io/docs/prometheus/latest/querying/examples/)
本章完整收录官方 examples 页面的全部场景,每条示例配解释。
### 4.1 简单时序选择
#### 4.1.1 按指标名选择
```promql
http_requests_total
```
返回所有 metric 为 `http_requests_total` 的时间序列。
> 💡 补充:这是一条**裸 metric name selector**。在生产环境中它可能展开为数千条不同标签的时间序列——这正是 1.10 节强调「先在表格视图构建查询」的典型场景。
#### 4.1.2 按多个标签过滤
```promql
http_requests_total{job="apiserver", handler="/api/comments"}
```
返回带给定 `job` 与 `handler` 标签的所有 `http_requests_total` 时间序列。
#### 4.1.3 取一段时间范围(range vector)
```promql
http_requests_total{job="apiserver", handler="/api/comments"}[5m]
```
为同一向量返回一整段时间范围(此处为截至查询时间的 5 分钟),使其成为 **range vector**。
**注意**:结果为 range vector 的表达式**不能直接绘图**,需在 expression browser 的表格("Console")视图中查看。
#### 4.1.4 正则匹配 job
```promql
http_requests_total{job=~".*server"}
```
用正则表达式仅选择 name 匹配某模式的 jobs(此处为所有以 `server` 结尾的 jobs)。
#### 4.1.5 反向正则排除状态码
```promql
http_requests_total{status!~"4.."}
```
选择除 4xx 外的所有 HTTP 状态码。
### 4.2 子查询示例
#### 4.2.1 5 分钟 rate 的 30 分钟序列
```promql
rate(http_requests_total[5m])[30m:1m]
```
返回 `http_requests_total` 过去 30 分钟的 5-minute rate,分辨率 1 分钟。
#### 4.2.2 嵌套子查询
```promql
max_over_time(deriv(rate(distance_covered_total[5s])[30s:5s])[10m:])
```
嵌套 subquery 示例。`deriv` 函数的 subquery 使用**默认 resolution**。注意不必要地使用 subqueries 是**不明智**的。
> 引自:[Query examples](https://prometheus.io/docs/prometheus/latest/querying/examples/)
### 4.3 函数与运算符实战
#### 4.3.1 每秒请求速率
```promql
rate(http_requests_total[5m])
```
返回所有 `http_requests_total` 时间序列在过去 5 分钟测得的每秒速率。
#### 4.3.2 按 job 聚合减少序列数
假设所有 `http_requests_total` 时间序列都有 `job`(按 job 名 fanout)与 `instance`(按 job 实例 fanout)标签,可对全部实例的 rate 求和以减少输出序列数但保留 `job` 维度:
```promql
sum by (job) (
rate(http_requests_total[5m])
)
```
#### 4.3.3 二元运算:未使用内存
若有两个具相同维度标签的不同指标,可对其施加二元运算符,两侧标签集相同的元素会匹配并传播到输出。例如返回(虚构 cluster scheduler 暴露的指标下)每个实例的未使用内存(MiB):
```promql
(instance_memory_limit_bytes - instance_memory_usage_bytes) / 1024 / 1024
```
同一表达式按 application 求和可写为:
```promql
sum by (app, proc) (
instance_memory_limit_bytes - instance_memory_usage_bytes
) / 1024 / 1024
```
#### 4.3.4 topk:按应用与进程类型取 CPU 使用 top 3
给定如下形式的每实例 CPU 使用指标(示例含 `app` 为 lion/elephant/turtle/fox,`proc` 为 web/worker/api/widget,另有 `rev`、`env`、`job="cluster-manager"` 标签):
```promql
topk(3, sum by (app, proc) (rate(instance_cpu_time_ns[5m])))
```
可按 application(`app`)与 process type(`proc`)分组取 CPU 使用 top 3。
#### 4.3.5 count:统计运行实例数
假设该指标每个运行实例一条时间序列,可按 application 统计运行实例数:
```promql
count by (app) (instance_cpu_time_ns)
```
#### 4.3.6 limitk:探查指标的标签
若为探查某指标以获得其标签(例如便于随后对其中一些聚合),可使用:
```promql
limitk(10, app_foo_metric_bar)
```
#### 4.3.7 limit_ratio:均匀采样约 10%
若希望返回的时间序列采样更均匀,可用以下方式获取约 10%:
```promql
limit_ratio(0.1, app_foo_metric_bar)
```
> 💡 补充:`limitk` 与 `limit_ratio` 均为实验性聚合运算符,须 `--enable-feature=promql-experimental-functions` 启用(见 2.8 节)。二者都以**确定性伪随机**方式选取子集,与样本类型无关。
> 引自:[Query examples](https://prometheus.io/docs/prometheus/latest/querying/examples/)
#### 4.3.8 查询章节索引页说明
官方 `querying/index.md`(<https://prometheus.io/docs/prometheus/latest/querying/>)为查询章节的**索引/落地页**,**仅有 frontmatter**(`title: Querying`、`sort_rank: 4`),本身不承载内容要点,具体内容分布在其子页面(basics、examples、operators、functions 等)中。
> 💡 补充:本页 URL 与同目录 `examples.md` 的 `sort_rank` 相同(均为 4),存在 sort_rank 重复(属官方文档细节,不影响内容)。
---
## 第 5 章 HTTP API
> 引自:[HTTP API](https://prometheus.io/docs/prometheus/latest/querying/api/)
当前稳定 HTTP API 挂在 Prometheus 服务器的 `/api/v1`,非破坏性新增都加在该前缀下。
### 5.1 通用格式与响应信封
#### 5.1.1 OpenAPI 规范
```text
GET /api/v1/openapi.yaml
```
- 机器可读规范,默认返回 **OpenAPI 3.1**;加 `?openapi_version=3.2` 返回 **OpenAPI 3.2**(含 `/api/v1/notifications/live` 等端点)。
- 可用于生成客户端、校验请求/响应、生成交互式文档。
#### 5.1.2 响应信封与状态码
响应体为 JSON,成功一律 `2xx`。
| 状态码 | 含义 |
|:-------|:-----|
| `400 Bad Request` | 参数缺失/错误 |
| `422 Unprocessable Entity` | 表达式无法执行(RFC4918) |
| `503 Service Unavailable` | 查询超时/中止 |
端点之前发生的错误可能返回其他非 2xx。
**信封字段**:
| 字段 | 说明 |
|:-----|:-----|
| `status` | `"success"` 或 `"error"` |
| `data` | 数据体 |
| `errorType` | 仅出错时含 |
| `error` | 仅出错时含 |
| `warnings` | 可选,字符串数组,不影响执行 |
| `infos` | 可选,info 级注解 |
出错时 `data` 仍可能含已成功收集的数据。
#### 5.1.3 占位符与参数约定
| 占位符 | 说明 |
|:-------|:-----|
| `<rfc3339 \| unix_timestamp>` | 输入支持 RFC3339 或 Unix 秒(可带小数);**输出时间戳始终为 Unix 秒** |
| `<series_selector>` | 需 URL 编码,如 `http_requests_total{method=~"(GET\|POST)"}` |
| `<duration>` | 如 `5m` |
| `<bool>` | `true` / `false` |
**时间格式说明**:输入时间既可用 **RFC3339**(如 `2015-07-01T20:10:51.781Z`)也可用 **Unix 时间戳**(秒,可带小数);**输出时间戳始终为 Unix 秒**。可重复的查询参数名以 `[]` 结尾。
### 5.2 表达式查询端点
#### 5.2.1 瞬时查询
```text
GET /api/v1/query
POST /api/v1/query
```
| 参数 | 类型 | 说明 |
|:-----|:-----|:-----|
| `query` | string | 必填,PromQL 表达式 |
| `time` | `<rfc3339 \| unix_timestamp>` | 可选,缺省用服务器当前时间 |
| `timeout` | `<duration>` | 可选,默认且受 `-query.timeout` 上限约束 |
| `limit` | number | 可选,最大返回序列数,`0` 表示禁用,不影响 scalar/string,仅截断 matrix/vector |
| `lookback_delta` | `<duration\|float>` | 可选,覆盖 lookback 期 |
| `stats` | string | 可选,`true` 基础统计、`all` 含逐步统计;其他非空值已废弃,会加 warning 并在下个大版本拒绝 |
- POST 时用 `Content-Type: application/x-www-form-urlencoded` 把参数放请求体,规避 URL 长度限制。
- **data 结构**:`{"resultType": "matrix"|"vector"|"scalar"|"string", "result": <value>}`。
**示例**:
```bash
curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'
```
返回 vector,元素为 `{"metric": {...}, "value": [1435781451.781, "1"]}`。
#### 5.2.2 范围查询
```text
GET /api/v1/query_range
POST /api/v1/query_range
```
| 参数 | 类型 | 说明 |
|:-----|:-----|:-----|
| `query` | string | 必填 |
| `start` | `<rfc3339 \| unix_timestamp>` | 必填,**含** |
| `end` | `<rfc3339 \| unix_timestamp>` | 必填,**含** |
| `step` | `<duration \| float>` | 分辨率步长,两种格式均可 |
| `timeout` | `<duration>` | 语义同瞬时查询 |
| `limit` | number | 语义同瞬时查询 |
| `lookback_delta` | `<duration\|float>` | 语义同瞬时查询 |
| `stats` | string | 语义同瞬时查询 |
data 的 `resultType` **恒为 `matrix`**。
**示例**:
```bash
curl 'http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s'
```
结果元素含 `values: [[<unix_time>, "<sample_value>"], ...]`。
#### 5.2.3 查询统计(stats)
`stats` 被设置时,data 内附 `stats` 对象:
| 字段 | 说明 |
|:-----|:-----|
| `timings` | 各阶段耗时秒,如 `evalTotalTime`、`execQueueTime` |
| `samples.totalQueryableSamples` | **载入**样本总数,范围向量函数每步计入完整窗口 |
| `samples.totalQueryableSamplesPerStep` | 仅 `stats=all` 且启用逐步统计 |
| `samples.samplesRead` | **读取**/I/O 样本数,范围查询的范围向量函数仅计每步新增点,其他查询等于 totalQueryableSamples |
| `samples.samplesReadPerStep` | 逐步读取数 |
| `samples.peakSamples` | 内存峰值 |
另暴露指标 `prometheus_engine_query_samples_total`、`prometheus_engine_query_samples_read_total`;逐步统计受 `promql-per-step-stats` feature flag 控制。
#### 5.2.4 格式化表达式
```text
GET /api/v1/format_query
POST /api/v1/format_query
```
- 参数仅 `query=<string>`。
- data 为格式化后的字符串,**注释会被移除**。
**示例**:`query=foo/bar` → `"foo / bar"`。
#### 5.2.5 解析表达式为 AST(实验性)
```text
GET /api/v1/parse_query
POST /api/v1/parse_query
```
- 参数 `query`。
- 端点名与返回结构可能随版本变化,**仅供 Prometheus 自带 Web UI 使用**。
- data 为 AST 字符串(示例给出 `binaryExpr`/`vectorSelector`/`matchers` 等字段)。
#### 5.2.6 表达式查询结果格式
`<sample_value>` 为数值;因 JSON 不支持 `NaN`/`Inf`/`-Inf`,样本值以**带引号字符串**传输。`histogram`/`histograms` 键仅在存在原生直方图时出现。
**Range vectors(matrix)**:
```json
[
{
"metric": {},
"values": [[<unix_time>, "<sample_value>"], ...],
"histograms": [[<unix_time>, <histogram>], ...]
}
]
```
- 每条序列可有 `values`、`histograms` 或两者;同一时间戳只会有一个 float 或 histogram 样本。
- 序列按 `metric` 排序,`sort`/`sort_by_label` 对范围向量无效。
**Instant vectors(vector)**:
```json
[
{
"metric": {},
"value": [<unix_time>, "<sample_value>"],
"histogram": [<unix_time>, <histogram>]
}
]
```
- `value` 与 `histogram` 二者**不同时出现**。无函数时顺序不保证。
**Scalars(scalar)**:`[ <unix_time>, "<scalar_value>" ]`
**Strings(string)**:`[ <unix_time>, "<string_value>" ]`
**Native histograms 的 `<histogram>`**:
```json
{
"count": "<count_of_observations>",
"sum": "<sum_of_observations>",
"buckets": [[<boundary_rule>, "<left_boundary>", "<right_boundary>", "<count_in_bucket>"], ...]
}
```
`boundary_rule` 取 0–3:
| 值 | 含义 |
|:--:|:-----|
| 0 | open left |
| 1 | open right |
| 2 | open both |
| 3 | closed both |
当前实现中正桶为 open left、负桶为 open right、零桶为 closed both(左边界负、右边界正)。
### 5.3 元数据查询端点
> **注意**:这些端点可能返回在所选时间范围内无样本、或已被删除 API 标记删除的序列的元数据;额外返回范围属实现细节,可能变化。
#### 5.3.1 按标签匹配器查序列
```text
GET /api/v1/series
POST /api/v1/series
```
| 参数 | 说明 |
|:-----|:-----|
| `match[]=<series_selector>` | **可重复,至少一个** |
| `start`、`end` | 时间范围 |
| `limit` | 可选,`0` 禁用 |
data 为对象数组,每项是标识序列的标签名/值对。`start`/`end` 只是**近似**,结果可能含区间内无样本的序列。
**示例**:
```bash
curl 'http://localhost:9090/api/v1/series' --data-urlencode 'match[]=up' --data-urlencode 'match[]=process_start_time_seconds{job="prometheus"}'
```
#### 5.3.2 标签名列表
```text
GET /api/v1/labels
POST /api/v1/labels
```
参数:`start`、`end`、`match[]`(均可选)、`limit`。data 为字符串标签名数组(示例含 `__name__`、`job`、`instance`、`le`、`quantile` 等)。
#### 5.3.3 标签值列表
```text
GET /api/v1/label/<label_name>/values
```
(**仅 GET**)。参数:`start`、`end`、`match[]`、`limit`。data 为字符串标签值数组。
**Values Escaping(标签名转义)**:标签名含 `/` 等字符时须转义——
- 前缀 `U__`;
- 字母/数字/冒号原样;
- 单下划线变双下划线;
- 其他字符用 `_<UTF-8编码点十六进制>_`(空格→`_20_`,`.`→`_2e_`)。
示例:`http.status_code` → `/api/v1/label/U__http_2e_status_code/values`。
#### 5.3.4 搜索 metric/label 名与值(实验性,流式)
```text
GET|POST /api/v1/search/metric_names
GET|POST /api/v1/search/label_names
GET|POST /api/v1/search/label_values
```
- 须启用 `--enable-feature=search-api`。
- 返回 **NDJSON**(`application/x-ndjson`):0+ 条 batch 行(含 `results` 数组、可选 `warnings`),然后以 trailer 行(`status`、`has_more`、可选 `warnings`)**或** error 行(`status`、`errorType`、`error`)结尾。
- 流开始**前**出错返回常规 JSON 错误 + 4xx/5xx,开始**后**出错以 NDJSON error 行替代 trailer。
- 客户端须容忍无 trailer 的突然 EOF,并忽略未知字段。
- `has_more` 仅信息性,本版无分页游标(需更多结果就提高 `limit`,受 `--web.search.max-limit` 限制,或用 `match[]` 收窄)。
**公共参数**:
| 参数 | 说明 |
|:-----|:-----|
| `match[]` | 标签匹配器 |
| `search[]` | 多值 OR |
| `fuzz_threshold` | 0–100 |
| `fuzz_alg` | `subsequence` \| `jarowinkler`,默认 `subsequence` |
| `case_sensitive` | 大小写敏感 |
| `sort_by` | 排序字段 |
| `sort_dir` | `asc` \| `dsc`,仅当 `sort_by=alpha` |
| `include_score` | 是否含分数 |
| `start`、`end` | 时间范围 |
| `limit` | 默认 100 |
| `batch_size` | 默认 100 |
**专属参数**:
- metric_names 加 `include_metadata`、`sort_by=alpha|score`;
- label_names 加 `sort_by=alpha|score`;
- label_values 加 `label=<label_name>`(**必填**)、`sort_by=alpha|score`。
数据按固定大小 block(通常每块 2 小时)存储,结果可能含窗口略外的活跃序列值。
#### 5.3.5 查询 exemplars(实验性)
```text
GET /api/v1/query_exemplars
POST /api/v1/query_exemplars
```
参数:`query`、`start`、`end`。data 为数组,元素含 `seriesLabels` 与 `exemplars`(每项 `labels`、`value` 字符串、`timestamp` 浮点秒)。
> 💡 补充:exemplar 是「样本之外挂载的追踪 ID 等元数据」,需 scrape/exposition 侧支持 OpenMetrics 的 exemplar 才会被采集。
### 5.4 目标与抓取池端点
#### 5.4.1 抓取池列表
```text
GET /api/v1/scrape_pools
```
data 为 `{"scrapePools": ["prometheus", "node_exporter", "blackbox"]}`。*v2.42 新增*
#### 5.4.2 抓取池配置(实验性,仅 Web UI 用)
```text
GET /api/v1/scrape_pools/config?scrapePool=<scrape_pool_name>
```
- `scrapePool` **必填**。
- 返回该池生效配置(含 `scrape_config_files` 加载的内容),**凭证与密钥被脱敏**。
- data 为 `{"yaml": "..."}`。
#### 5.4.3 目标总览
```text
GET /api/v1/targets
```
- 默认同时返回 active 与 dropped targets;dropped 受 `keep_dropped_targets` 限制。
- `labels` 是 **relabel 后**的标签集,`discoveredLabels` 是服务发现阶段**未修改**的标签。
- activeTargets 每项含 `discoveredLabels`、`labels`、`scrapePool`、`scrapeUrl`、`globalUrl`、`lastError`、`lastScrape`、`lastScrapeDuration`、`health`、`scrapeInterval`、`scrapeTimeout`。
**参数**:
| 参数 | 说明 |
|:-----|:-----|
| `state` | `active` \| `dropped` \| `any`,过滤后被排除的仍返回空数组,其他值忽略 |
| `scrapePool` | 按抓取池名过滤 |
> 💡 补充:文档示例中 `lastScrapeDuration` 出现两种口径(`0.050688943` 秒与 `50688943` 纳秒),官方未说明,疑为文档笔误/版本差异。使用时建议以实际返回为准。
#### 5.4.4 Relabel 步骤(实验性,仅 Web UI 用)
```text
GET /api/v1/targets/relabel_steps
```
参数:`scrapePool=<string>`(必填)、`labels=<string>`(必填,relabel 前的标签集 JSON)。data.steps 每步含 `keep`、`output`、`rule`(`action`/`regex`/`replacement`/`separator`/`target_label`/`source_labels`)。
### 5.5 规则与告警端点
#### 5.5.1 规则列表
```text
GET /api/v1/rules
```
返回已加载的 alerting 与 recording 规则,以及各告警规则当前触发的活跃告警。**该端点比 API v1 整体稳定性保证弱**。
| 参数 | 说明 |
|:-----|:-----|
| `type` | `alert` \| `record` |
| `rule_name[]` | 按规则名过滤 |
| `rule_group[]` | 按规则组过滤 |
| `file[]` | 按文件过滤 |
| `exclude_alerts` | `<bool>` |
| `match[]` | `<label_selector>`,匹配规则**定义中的标签**,非模板展开后的值 |
| `group_limit` | `<number>`,超出则响应含 `groupNextToken` |
| `group_next_token` | 须与 `group_limit` 同时使用;若分页中对应规则组被删则返回 400 |
分页期间规则组被修改时不保证一致性。
- data.groups[].rules[] 中 alerting 规则含 `alerts`(`activeAt`、`annotations`、`labels`、`state`、`value`)、`annotations`、`duration`、`health`、`labels`、`name`、`query`、`type`;
- 组含 `file`、`interval`、`limit`、`name`。
#### 5.5.2 活跃告警
```text
GET /api/v1/alerts
```
data.alerts[] 每项含 `activeAt`、`annotations`、`labels`、`state`、`value`。稳定性保证同样**弱于** API v1。
### 5.6 指标元数据端点
#### 5.6.1 目标指标元数据(实验性)
```text
GET /api/v1/targets/metadata
```
| 参数 | 说明 |
|:-----|:-----|
| `match_target` | `<label_selectors>`,留空选全部目标 |
| `metric` | `<string>`,留空取全部 |
| `limit` | number |
**仅返回直接从目标抓取的元数据**,经 Remote-Write 或 OTLP 送入的**不含在内**。data 为对象数组,含 `target`、`metric`、`type`、`help`、`unit`。
#### 5.6.2 指标元数据(实验性)
```text
GET /api/v1/metadata
```
参数:`limit`、`limit_per_metric`、`metric`。data 为对象,键为指标名、值为该指标跨所有目标的唯一元数据对象列表(同一指标可能存在多个不同 `help` 的条目)。
### 5.7 Alertmanager 与状态端点
#### 5.7.1 Alertmanager 发现
```text
GET /api/v1/alertmanagers
```
data 含 `activeAlertmanagers` 与 `droppedAlertmanagers`,每项为 `{"url": "..."}`。
#### 5.7.2 状态端点
| 端点 | 说明 | 新增版本 |
|:-----|:-----|:---------|
| `GET /api/v1/status/config` | 返回已加载配置的 dump YAML,**YAML 注释不保留**。data 为 `{"yaml": "..."}` | — |
| `GET /api/v1/status/flags` | 返回所有 flag 值,**全部为 string 类型**(如 `query.lookback-delta`、`query.max-concurrency`) | v2.2 |
| `GET /api/v1/status/runtimeinfo` | 运行时属性(`startTime`、`CWD`、`hostname`、`serverTime`、`reloadConfigSuccess`、`lastConfigTime`、`timeSeriesCount`、`corruptionCount`、`goroutineCount`、`GOMAXPROCS`、`GOGC`、`GODEBUG`、`storageRetention`),类型不一,且可能随版本无预警变化 | v2.14 |
| `GET /api/v1/status/buildinfo` | 构建信息(`version`、`revision`、`branch`、`buildUser`、`buildDate`、`goVersion`),全部 string,且可能随版本无预警变化 | v2.14 |
| `GET /api/v1/status/tsdb` | TSDB 基数统计,参数 `limit`(每类统计返回条数,默认 10,最大 10000) | v3.6.0 |
| `GET /api/v1/status/tsdb/blocks` | 当前加载的 TSDB block 及元数据(`ulid`、`minTime`/`maxTime` 毫秒、`stats`{`numSeries`/`numSamples`/`numChunks`}、`compaction`{`level`/`sources`}、`version`)。实验性,格式可能变化 | v2.15 |
| `GET /api/v1/status/walreplay` | WAL 回放信息。该端点在服务器标记 ready 前即可用并实时更新 | v2.28 |
| `GET /api/v1/status/self_metrics` | 实验性。以结构化 JSON(ProtoJSON 编码的 `io.prometheus.client.MetricFamily`)返回自身插桩指标(与 `/metrics` 同源),参数 `metric_name_pattern=<string>`(完全锚定的正则,可选) | — |
**`/api/v1/status/tsdb` 的 data 字段**:
- `headStats`(`numSeries`、`chunkCount`、`minTime`、`maxTime`,毫秒);
- `seriesCountByMetricName`、`labelValueCountByLabelName`、`memoryInBytesByLabelName`、`seriesCountByLabelValuePair`。
> 💡 补充:官方文档正文列出的键名为 `seriesCountByLabelPair`,而示例中为 `seriesCountByLabelValuePair`,**文档自相矛盾**。
**`/api/v1/status/walreplay` 的字段**:返回示例字段为 `min`/`max`/`current`/`state`,而正文描述为 `read`/`total`/`progress`/`state`(`waiting` | `in progress` | `done`)。**描述与示例键名不一致**。
**`/api/v1/status/self_metrics`** 每个 family 含 `name`、`help`、`type`(`COUNTER`/`GAUGE`/`SUMMARY`/`HISTOGRAM`/`UNTYPED`)、可选 `unit`、`metric` 列表(含 `label` 及类型专用数据)。
### 5.8 TSDB 管理 API
**未设置 `--web.enable-admin-api` 时这些端点不启用。**
#### 5.8.1 Snapshot
```text
POST|PUT /api/v1/admin/tsdb/snapshot
```
- 把当前数据快照到 TSDB 数据目录下 `snapshots/<datetime>-<rand>`。
- 参数 `skip_head=<bool>`(可选,跳过仅在 head block、尚未 compact 到磁盘的数据)。
- data 为 `{"name": "<快照目录名>"}`。
- *v2.1 新增,v2.9 起支持 PUT*
#### 5.8.2 Delete Series
```text
POST|PUT /api/v1/admin/tsdb/delete_series
```
- 参数:`match[]`(可重复,至少一个)、`start`(可选,默认最早)、`end`(可选,默认最晚);start/end 都不给会清除匹配序列的全部数据。
- 成功返回 `204`。
- 实际数据仍在磁盘,待后续 compaction 清理或调用 clean_tombstones。
- **注意**:该端点只是把样本**标记删除**,不保证元数据查询中不再返回该区间序列元数据。
- *v2.1 新增,v2.9 起支持 PUT*
#### 5.8.3 Clean Tombstones
```text
POST|PUT /api/v1/admin/tsdb/clean_tombstones
```
- 无参数无 body,成功返回 `204`。
- *v2.1 新增,v2.9 起支持 PUT*
### 5.9 写入/读取接收端
#### 5.9.1 Remote Write Receiver
`--web.enable-remote-write-receiver` 启用后端点 `/api/v1/write`。**不视为高效摄入方式**,仅适合特定低量场景,不可替代抓取。*v2.33 新增*
#### 5.9.2 Remote Read
```text
POST /api/v1/read
```
供外部系统(如 Thanos)读取 TSDB,详见 5.11 节与本手册独立 Remote Read API 说明。
#### 5.9.3 OTLP Receiver
`--web.enable-otlp-receiver` 启用后端点 `/api/v1/otlp/v1/metrics`。*v2.47 新增*。启用 `--enable-feature=otlp-deltatocumulative`(用 OTel Collector 的 deltatocumulative)可把 delta 时序转为 cumulative 等价形式。*v3.2 新增*
### 5.10 通知与特性端点
| 端点 | 说明 | 新增版本 |
|:-----|:-----|:---------|
| `GET /api/v1/notifications` | 实验性,返回当前所有活跃通知(`text`、`date`、`active`) | v3.0 |
| `GET /api/v1/notifications/live` | 实验性,以 Server-Sent Events 流式推送实时通知;删除的通知以 `active: false` 发送,连接时会先发活跃通知。订阅者达上限时返回 `204 No Content`,上限由 `--web.max-notifications-subscribers`(默认 16)控制 | v3.0 |
| `GET /api/v1/features` | 返回服务器已启用/禁用的特性,按类别组织 | v3.8 |
**`/api/v1/features` 的类别**:`api`、`promql`、`promql_functions`、`promql_operators`、`rules`、`scrape`、`service_discovery`、`templating`、`tsdb`、`otlp_receiver`、`prometheus` 等。data 为「类别 → 特性名 → 布尔」映射。
**注意**:feature 名一律 `snake_case`;值为 `false` 的可能被省略,客户端应把缺失视为 `false`;须忽略未知特性名与类别。
### 5.11 Remote Read API
> 引自:[Remote Read API](https://prometheus.io/docs/prometheus/latest/querying/remote_read_api/)
#### 5.11.1 稳定性声明
**该 API 当前不属于稳定 API**,即使在 Prometheus 的非主版本(non-major)发布之间也可能发生变化。
#### 5.11.2 功能与端点
- 提供从 Prometheus **读取数据**的能力。接口**要求 snappy 压缩**。
- API 定义位于 `github.com/prometheus/prometheus` 仓库的 `prompb/remote.proto`;Protobuf 定义也可在 buf.build 上查看(`prometheus.ReadRequest`)。
- 端点:请求发往 `/api/v1/read`。
#### 5.11.3 External labels 与 remote read(双向影响)
`global` 中的 `external_labels` 设置会**双向**影响 remote read:
| 方向 | 行为 |
|:-----|:-----|
| **对外提供 remote read(服务端侧)** | Prometheus 会把 `external_labels` 追加到经 `/api/v1/read` 返回的每一条时间序列上。这些标签**不存储在 TSDB 中**,而是在**响应时**添加。对于与某个 external label 匹配的**入站等值匹配器(equality matchers)**,会被改写为匹配空字符串,以便 TSDB 能完成查询 |
| **使用 remote read(客户端侧)** | 发起查询的 Prometheus 会把自身的 `external_labels` 作为**额外的等值匹配器**放进请求,随后再从响应中把它们剥离 |
**风险提示**:若查询方 Prometheus 的 `external_labels` 与远端服务器不同或缺失,查询可能**返回空结果**,因为匹配器对不上。应确保查询方所有 `external_labels` 与远端实际存在的标签保持一致。
#### 5.11.4 Samples 与 Streamed Chunks
- **Samples**:返回的消息包含与请求查询匹配的**原始样本列表**。
- **Streamed Chunks**:流式 chunk 使用受 [Gorilla 论文] 启发的 **XOR 算法**编码,但**提供毫秒级分辨率**(而非秒级)。
---
## 第 6 章 查询调试
> 引自:[Using the Prometheus query log](https://prometheus.io/docs/guides/query-log/)
### 6.1 是什么 / 为什么
- 自 **2.16.0** 起,Prometheus 能够把所有**引擎执行的查询**记录到日志文件。
- query log 可在**运行时**切换开关,因此适合在排查 Prometheus 实例变慢或高负载时启用。
- 用途:定位「哪条 PromQL 在拖慢服务器」——通常来自仪表盘或临时调试查询。
### 6.2 怎么用:开启与关闭
启用/禁用两步:**① 修改配置以添加/移除 query log 配置;② reload Prometheus server 配置。**
#### 6.2.1 开启
记录所有查询到 `/prometheus/query.log`(假设 `/prometheus` 是数据目录且 Prometheus 有写权限)。修改 `prometheus.yml`:
```yaml
global:
scrape_interval: 15s
evaluation_interval: 15s
query_log_file: /prometheus/query.log
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
```
reload:
```bash
curl -X POST http://127.0.0.1:9090/-/reload
```
> 💡 补充:若 Prometheus 启动时未带 `--web.enable-lifecycle`,且非 Windows 环境,可向 Prometheus 进程发送 **SIGHUP** 触发 reload。
>
> 💡 补充:命令行参数 `--query.log-file` 等价于配置中的 `query_log_file`;两者其一即可,生产上更推荐配置方式以便配合 reload 动态切换。
#### 6.2.2 关闭
重复上述操作并从配置中移除 `query_log_file`,然后 reload。
#### 6.2.3 验证是否启用
两个 metrics:
```text
# HELP prometheus_engine_query_log_enabled State of the query log.
# TYPE prometheus_engine_query_log_enabled gauge
prometheus_engine_query_log_enabled 0
# HELP prometheus_engine_query_log_failures_total The number of query log failures.
# TYPE prometheus_engine_query_log_failures_total counter
prometheus_engine_query_log_failures_total 0
```
- `prometheus_engine_query_log_enabled`:启用时为 `1`,否则为 `0`。
- `prometheus_engine_query_log_failures_total`:表示无法记录的查询数。
### 6.3 日志格式与字段解读
query log 为 **JSON 格式**(每行一条 JSON)。
#### 6.3.1 查询字段概览
```json
{
"params": {
"end": "2020-02-08T14:59:50.368Z",
"query": "up == 0",
"start": "2020-02-08T13:59:50.368Z",
"step": 5
},
"stats": {
"timings": {
"evalTotalTime": 0.000447452,
"execQueueTime": 7.599e-06,
"execTotalTime": 0.000461232,
"innerEvalTime": 0.000427033,
"queryPreparationTime": 1.4177e-05,
"resultSortTime": 6.48e-07
}
},
"ts": "2020-02-08T14:59:50.387Z"
}
```
| 字段 | 含义 |
|:-----|:-----|
| `params` | 查询:`start`/`end` 时间戳、`step` 与实际查询语句 `query` |
| `stats` | 统计(当前含内部引擎计时器) |
| `ts` | 查询**结束**时间戳 |
`stats.timings` 各计时器:
| 计时器 | 含义 |
|:-------|:-----|
| `queryPreparationTime` | 查询准备(解析、构造执行计划)耗时 |
| `execQueueTime` | 在执行队列中等待的耗时 |
| `innerEvalTime` | 引擎实际求值耗时 |
| `evalTotalTime` | 求值总耗时 |
| `execTotalTime` | 执行总耗时 |
| `resultSortTime` | 结果排序耗时 |
> 💡 补充:视触发请求来源不同,JSON 行还会含额外字段。
#### 6.3.2 API 查询与 consoles
HTTP 请求含客户端 IP、method、path:
```json
{
"httpRequest": {
"clientIP": "127.0.0.1",
"method": "GET",
"path": "/api/v1/query_range"
}
}
```
- `path` 在设置了 web prefix 时会包含前缀,也可能指向 console。
- client IP 是**网络 IP 地址**,**不考虑** `X-Forwarded-For` 等 header;若想记录代理后的原始调用方,需**在代理本身记录**。
#### 6.3.3 Recording rules 与 alerts
含 `ruleGroup` 元素(文件路径与 group 名):
```json
{
"ruleGroup": {
"file": "rules.yml",
"name": "partners"
}
}
```
### 6.4 日志轮转注意事项
- **Prometheus 自身不会轮转 query log**,需用**外部工具**,如 `logrotate`(多数 Linux 发行版默认启用)。
- 示例 `/etc/logrotate.d/prometheus`:
```text
/prometheus/query.log {
daily
rotate 7
compress
delaycompress
postrotate
killall -HUP prometheus
endscript
}
```
- 该配置每日轮转并保留一周历史。
> 💡 补充:`postrotate` 里的 `killall -HUP prometheus` 与 6.2.1 的 reload 机制一致——Prometheus 收到 SIGHUP 会重新打开日志文件句柄,从而切换到新的 query log 文件,避免继续写入已被 logrotate 改名的旧文件。
### 6.5 性能排查思路
> 💡 补充:以下为结合官方 query log / stats 的排查方法,非文档原文。
1. **先开 query log,再复现问题**:在实例变慢时才启用(运行时切换),避免长期开启带来的 I/O 开销。
2. **按 `stats.timings` 排序定位瓶颈**:
- `execQueueTime` 高 → 并发查询排队,考虑 `--query.max-concurrency` 与客户端并发;
- `innerEvalTime` 高 → 单条表达式本身昂贵,需优化 PromQL(加过滤、先聚合、用 recording rule);
- `resultSortTime` 高 → 返回序列数过多,用 `limit` 或更强聚合收窄。
3. **看 `params.query` 与 `httpRequest.path`**:
- 若来自 `/api/v1/query_range` 且 step 很小、时间范围很大,通常是**仪表盘**在拉大图;
- 若是 `query_exemplars`/`series` 等元数据类端点,往往是**查询构建器(如 Grafana 的指标浏览器)**在探测。
4. **结合 HTTP API 的 `stats` 参数**(见 5.2.3):用 `stats=all` 获取 `totalQueryableSamples`、`samplesRead`、`peakSamples`,判断是**载入样本多**还是**内存峰值高**。
5. **落地优化**:对高频昂贵查询建立 **recording rule** 预计算;对仪表盘收紧时间范围与 step;必要时调整 `--query.timeout`、`--query.max-concurrency`。
---
## 第 7 章 直方图与分位数
> 引自:[Histograms and summaries](https://prometheus.io/docs/practices/histograms/)
### 7.1 总纲与两种表示法
**总纲**:histogram 因历史原因分 **classic** 与 **native** 两种变体(后者还有子变体)。**能用的前提下优先用 native histograms,优于 classic histograms 和 summaries。** 仅在①存量指标是 classic/summary、②所用库不支持 native、③少数特定场景时退而求其次。OTel 接入时提示「prefer the latter(NHCB)」。
**表示分布的两条路**:
| 方式 | 机制 | 优点 | 缺点 |
|:-----|:-----|:-----|:-----|
| **summary** | 程序对预配置时间窗口**预计算**若干分位数并作为额外指标暴露 | 分位通常很精确 | (a) 计算有资源开销;(b) 之后**无法**换窗口/百分位重算;(c) **分位数无法聚合**(无法算多副本服务的整体 P90) |
| **histogram** | 以 digest(bucket 计数)表示分布 | 之后可对**任意窗口**算**任意**分位数;**分布可互相聚合** | 分位是估算(受桶/分辨率限制) |
两者都额外采集 **count** 与 **sum**;都需每样本采集一堆数值,称 **_composite types_**。
**classic 与 native 的映射差异**:
- **classic 映射**:把多值拆成**多条序列**,按特定 label 区分——summary 每个分位数、histogram 每个 bucket、及 count/sum 各一条。
- **native**:单个样本内含 count、sum 与动态 bucket(计数+边界),TSDB 中一个 histogram 只对应**一条**样本序列。
#### 7.1.1 库支持
- summary:**几乎都支持**(部分只给 count/sum,即 quantile-less summary,仍合法);
- classic histogram:**广泛**;
- native histogram:**仍罕见**,需 protobuf 格式(Java、Go),文本格式支持正作为 **OpenMetrics v2** 推进。
只暴露 classic 时,可让 Prometheus 摄入为 **NHCB(自定义桶边界的 native histogram)**——比标准指数桶有局限,但存储效率远高于 classic,PromQL 处理同 native,日后迁移容易。
#### 7.1.2 OTel 接入
普通 OTel histogram 可转 classic 或 NHCB(**优选 NHCB**);OTel _exponential histograms_ 总转成标准 **native histogram**。
### 7.2 Histogram 数据结构
#### 7.2.1 classic histogram 的序列构成
一个 classic histogram 指标(如 `http_request_duration_seconds`)在 TSDB 中映射为多条 float 序列:
| 序列 | 含义 |
|:-----|:-----|
| `http_request_duration_seconds_bucket{le="..."}` | 每个桶的**累积**计数,`le` = less or equal,值为累积桶**上界(闭区间)** |
| `http_request_duration_seconds_sum` | 观测值**总和** |
| `http_request_duration_seconds_count` | 观测**次数** |
> **关键**:classic 摄入时**每个配置桶都生成一条 `_bucket` 序列(无论是否被填充)**。
#### 7.2.2 native histogram 的样本结构
单个样本内含 count、sum 与动态 bucket(计数 + 边界),TSDB 中一个 histogram 只对应**一条**样本序列。HTTP API 中暴露为:
```json
{
"count": "<count_of_observations>",
"sum": "<sum_of_observations>",
"buckets": [[<boundary_rule>, "<left_boundary>", "<right_boundary>", "<count_in_bucket>"], ...]
}
```
### 7.3 count 与 sum:算平均值
思路:**先取 `rate`,再「sum 的 rate」除以「count 的 rate」。**
#### 7.3.1 native(含 NHCB)
用 `histogram_sum` / `histogram_count`:
```promql
histogram_sum(rate(http_request_duration_seconds[5m]))
/
histogram_count(rate(http_request_duration_seconds[5m]))
```
等价短写法:
```promql
histogram_avg(rate(http_request_duration_seconds[5m]))
```
#### 7.3.2 summary / classic
用 `_sum` / `_count` 序列:
```promql
rate(http_request_duration_seconds_sum[5m])
/
rate(http_request_duration_seconds_count[5m])
```
- 分母即**每秒请求数**;`..._count` 行为等价于请求计数器(若无该 histogram/summary 你会命名 `http_requests_total`)。
#### 7.3.3 负值观测的特殊问题
- 混入负值时 sum 可能下降,被 `rate(..._sum[5m])` 误判为 **counter reset**。
- **只影响 summary 与 classic**;**native(含 NHCB)整体做 rate,能正确检测 reset**。
- 万不得已时用两个 summary/histogram(正值、取反负值)再合并。
#### 7.3.4 聚合
sum/count **可加**,应在 **rate 之后、相除之前**聚合(与底层类型无关):
```promql
# native histograms
sum by (job) (histogram_sum(rate(http_request_duration_seconds[5m])))
/
sum by (job) (histogram_count(rate(http_request_duration_seconds[5m])))
# summaries / classic histograms
sum by (job) (rate(http_request_duration_seconds_sum[5m]))
/
sum by (job) (rate(http_request_duration_seconds_count[5m]))
```
### 7.4 Bucketing:桶选择
#### 7.4.1 classic
- 埋点配**固定桶边界**;classic 摄入时每个配置桶都生成一条 `_bucket` 序列。
- 桶越多**精度越高**,但「每桶一序列」**代价显著**。
- 难点:**事先挑对桶困难,事后改桶扰动大**。
#### 7.4.2 NHCB
- 未填充桶**代价可忽略**,已填充也更高效(单条复合序列)。
#### 7.4.3 native
- **不选边界**,配**分辨率**,按指数分桶动态创建,覆盖 -Inf 到 +Inf。
- 分辨率越高资源越大,但同资源下可达远高于 classic 的分辨率;库还提供**限桶策略**(偶发重置、自适应降分辨率)。
#### 7.4.4 计算落在某区间的比例
```promql
histogram_fraction(0, 0.3, sum by (job) (rate(http_request_duration_seconds[5m])))
```
即每个 job 最近 5m 落在 0–300ms 的比例(300ms 写 `0.3`,**始终用基础单位**)。
- `sum` 通过求和相应桶聚合;**桶布局不同会先 reconcile**——标准指数桶平滑回退到最低公共分辨率,5m 内跨时间的布局差异同样处理。
- NHCB 下效果重度依赖具体布局,**可能只剩一个含全部观测的桶**,需 reconcile 时结果带 **info 级 annotation**。
#### 7.4.5 精度
- 恰有桶边界在 0.3 才**精确**;否则**用插值返回估算比例**,分辨率越高越准。
- 已知 SLO(如 95% 请求 300ms 内)可用 classic 固定边界求精确,但改 SLO 后改桶很繁琐;native 动态桶虽不落在 0.3 但插值仍较准,换来**随时改区间边界的自由**(利于 SLO 变更)。
#### 7.4.6 纯 classic 的表达式截然不同
```promql
sum by (job) (rate(http_request_duration_seconds_bucket{le="0.3"}[5m]))
/
sum by (job) (rate(http_request_duration_seconds_count[5m]))
```
- `le` = less or equal,值为累积桶**上界(闭区间)**。
- **严格要求存在 0.3 桶边界**:无则**不做插值、直接无结果**;只有部分 histogram 有时返回不完整结果**且无警告**(很糟糕,应避免纯 classic)。
#### 7.4.7 Apdex score
目标 300ms、可容忍 1.2s(通常 4 倍目标)。按 job 求最近 5m:
**native(含 NHCB)**:
```promql
histogram_fraction(0, 0.3, sum by (job) (rate(http_request_duration_seconds[5m])))
+
histogram_fraction(0.3, 1.2, sum by (job) (rate(http_request_duration_seconds[5m]))) / 2
```
**纯 classic**(**必须**在精确边界有桶,因累积桶表达式不同):
```promql
(
sum by (job) (rate(http_request_duration_seconds_bucket{le="0.3"}[5m]))
+
sum by (job) (rate(http_request_duration_seconds_bucket{le="1.2"}[5m]))
)
/
2
/
sum by (job) (rate(http_request_duration_seconds_count[5m]))
```
> 💡 补充:原文注——为简便未显式排除失败请求,严格正确的 Apdex 需排除。
### 7.5 Quantiles:分位数
φ-quantile(0 ≤ φ ≤ 1)是 N 个观测中排第 φ*N 位的观测值;0.5 = 中位数,0.95 = P95。
#### 7.5.1 本质区别
- **summary**:在**程序内**流式算出并**直接暴露**;
- **histogram**:暴露**分桶计数**,由**服务端** `histogram_quantile()` 计算。
#### 7.5.2 三类实现对比
| 维度 | native | classic | summary |
|:-----|:-------|:--------|:--------|
| **埋点配置** | 选分辨率 / 限桶策略 | 选适合的桶 | 选 φ 与窗口(之后不可再算别的) |
| **埋点成本** | 便宜(仅自增计数) | 便宜 | 较贵(流式计算) |
| **查询性能** | 需服务端计算(太慢可用 recording rules) | 需服务端计算 | 快 |
| **序列数** | 一条复合样本 | `_sum`+`_count`+每桶 | `_sum`+`_count`+每分位 |
| **分位误差** | 受分辨率限 | 受桶宽限 | 可配、通常很低 |
| **φ 与窗口** | PromQL ad-hoc 指定 | PromQL ad-hoc 指定 | 埋点期预配 |
| **聚合** | 可行 | 可行(需桶边界不变) | **不可聚合** |
**NHCB**:埋点侧与 classic **完全相同**(NHCB 仅服务端生成);查询性能与序列数同 native,**分位误差同对应 classic**;改桶布局比 classic 略优雅但仍 problematic(至少以 annotation 标出)。
#### 7.5.3 多副本聚合
多副本想算整体 P95 时,聚合 summary 的分位数几乎无意义,取平均统计上荒谬:
```promql
avg(http_request_duration_seconds{quantile="0.95"}) // BAD!
```
histogram 则可聚合:
```promql
# native(含 NHCB)
histogram_quantile(0.95, sum(rate(http_request_duration_seconds[5m]))) // GOOD.
# classic
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m]))) // GOOD.
```
#### 7.5.4 SLO 变化时的取值
SLO 变化(改看 P90、或改看 10m)时**只改表达式**,无需重配埋点。取值示例:
- summary 配 0.95 分位 + 5m 衰减;
- native 配合适分辨率(Go 库可用 `NativeHistogramBucketFactor=1.1`);
- classic 配 `{le="0.1"}`、`{le="0.2"}`、`{le="0.3"}`、`{le="0.45"}`。
### 7.6 histogram_quantile 计算原理(线性插值)
`histogram_quantile(φ, b)` 的关键逻辑(函数详细签名见 3.7.4):
1. **识别桶归属**:classic 的 float 样本必须带 `le` 标签,其余标签与 metric 名用于识别各 bucket 属于哪个经典直方图。
2. **定位落点桶**:找到分位数落在哪个 bucket(累积计数首次达到 φ*count 的桶)。
3. **桶内插值**:
- 经典直方图、原生自定义边界直方图、原生 zero bucket → **线性插值**(假设桶内**均匀分布**);
- 原生标准指数 schema 的非零桶 → **指数插值**(假设样本在更高分辨率的假设直方图中均匀填充各桶)。
4. **特例处理**:见 3.7.4 的返回值特例、经典特例、原生特例。
5. **单调性修正**:classic bucket 计数应单调不减;浮点误差(阈值 1e-12)被忽略,非单调则抬升到前一桶值并打 info 注解 `input to histogram_quantile needed to be fixed for monotonicity`。
**聚合注意事项(务必记住)**:用 `rate()` 指定时间窗口后,**先把 `le` 放进 `by` 再求和**:
```promql
# classic:必须有 le
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
# native:直接 sum,无需 by
histogram_quantile(0.95, sum(rate(http_request_duration_seconds[5m])))
```
> 💡 补充:顺序**必须是 `rate` → `sum` → `histogram_quantile`**。若先 `sum` 后 `rate`,会因 counter reset 无法跨实例正确检测而出错(同 3.6.6 的通用规则)。
### 7.7 分位数估算误差
- 分位数无论程序内还是服务端算,都是**估算**。
**例 1(native,`NativeHistogramBucketFactor=1.1`,请求全约 220ms)**:
- 尖峰所在桶约 0.210–0.229;设全在此桶,`histogram_quantile` 插值估 **P95 = 228ms**,真值在 210–229ms 之间,最坏情况也相当准。
- 同例 classic(`{le="0.3"}`,即 200–300ms):插值估 **295ms**,真值 200–300ms——误差界大得多且离真值远;SLO=300ms 时会误以为快要违约。
**例 2(再加 100ms 后端延迟,尖峰 320ms)**:
- native 相关桶 297–324ms,估 **323ms**(几乎完美);
- classic 观测几乎全在 300–450ms 桶,估 **443ms**,远离正确的约 320ms。
**例 3(150ms 尖峰占 90%,另 10% 均匀铺在 150–450ms 长尾)**:此时 P95 恰为 SLO 300ms;
- classic 在此例**准确**(P95 恰与某桶边界重合;桶内均匀正是 classic 插值的假设)。
**summary 的误差**按 φ 维度配置,如 0.95±0.01 → 结果落在 P94–P96 之间;上例 P94=270ms、P96=330ms,报告值可能在 270–330ms,横跨「明显在 SLO 内」与「明显超 SLO」。
**结论**:
- summary **掌握 φ 维度的误差**;
- histogram **掌握观测值维度的误差**(classic 靠选桶布局、难;native 靠选分辨率、易)。
- 分布**越宽**,φ 微变引起观测值大幅偏差;分布**越尖**,观测值的小区间覆盖很大的 φ 区间。
#### 7.7.1 三条经验法则
1. **能用 native histograms 就用**,分辨率匹配精度要求,兼顾精度、可聚合与 ad-hoc 改参数(百分位、滑动窗口);
2. **不能 native 但需聚合则用 classic**,须设合适桶边界并在桶成本与所需精度间取平衡;
3. **仅当不需聚合才考虑 summary**(φ 维度很准、总成本较低,但须在埋点期选定分位数与窗口是严重缺点)。
### 7.8 Summary 的对比与取舍
| 取舍点 | summary | histogram |
|:-------|:--------|:----------|
| 分位精度 | φ 维度可配,通常很高 | 受桶宽/分辨率限制,是估算 |
| 可聚合性 | **不可**聚合 | **可**聚合 |
| φ 与窗口灵活性 | 埋点期固定,事后**不可改** | 查询期任意指定 |
| 埋点开销 | 较高(流式计算) | 较低(自增计数) |
| 查询开销 | 低 | 需服务端计算 |
| 序列数 | `_sum`+`_count`+每分位 | `_sum`+`_count`+每桶(classic)或一条(native) |
### 7.9 可视化
- summary 分位数可像**普通浮点序列**可视化;
- histogram 更复杂:
- UI 的 **_Table_ 视图**显示单个样本的图形表示;
- **_Graph_ 视图**对 classic 分别画每条成分序列、对 native **只画 sum**。
- 随时间变化的最佳可视化是 **heatmap**;Prometheus UI 尚不支持(issue #15346),**Perses、Grafana** 可渲染。
- classic 分辨率通常不足以做好热力图,native 的高分辨率可支持非常精细的热力图。
### 7.10 Native histograms 简要提点
- 单一复合样本:一个 histogram 对应**一条**序列(对比 classic 的「每桶一序列」),存储与基数双重友好。
- 动态指数分桶:只需配**分辨率**,覆盖 -Inf 到 +Inf,无需预先选桶边界。
- 可聚合:`sum` / `rate` 后可直接 `histogram_quantile`,无需 `by (le)`。
- 负值友好:整体做 rate,能正确检测 counter reset(classic/summary 不能)。
- NHCB:由 classic 摄入生成,PromQL 处理同 native,便于日后迁移。
- OTel 的 exponential histogram 总转成标准 native histogram。
- 规范详见第 4 篇「Native Histograms 规范」章节。
---
## 第 8 章 UTF-8 指标名
> 引自:[UTF-8 in Prometheus](https://prometheus.io/docs/guides/utf8/)
### 8.1 背景
- **3.0 之前**的 Prometheus 要求 metric 与 label 名遵守**严格字符集**;
- **Prometheus 3.0 起**所有 UTF-8 字符串都是**合法名称**,但生态其他部分引入任意 UTF-8 字符名称还需一些手动改动。
- 也可能有用户想**强制 legacy 字符集**(例如为兼容旧 Prometheus 或尚不支持 UTF-8 的其他 scraper)。本章讲解 UTF-8 过渡细节。
#### 8.1.1 Go Instrumentation
- 官方 client_golang 创建的 metrics 默认接受 UTF-8 名称。
- 此前文档建议用户覆盖 `model.NameValidationScheme` 值以选择 legacy 校验;该 boolean **现已 deprecated**,应始终设为 `UTF8Validation`。
- 若需强制 legacy 校验,应由各实现调用相应 validation APIs 完成,而不再是库特性。
#### 8.1.2 其他语言
其他 client libraries 可能**尚不支持 UTF-8**,可能需要特殊处理或配置,请查阅所用库文档。
### 8.2 配置项:metric_name_validation_scheme
Prometheus 3.0 默认接受所有 UTF-8 字符串为合法 metric/label 名;可覆盖此行为以拒绝不符合 legacy 字符集的名称。
**全局设置**:
```yaml
global:
metric_name_validation_scheme: legacy
```
**按 scrape config 设置**:
```yaml
scrape_configs:
- job_name: prometheus
metric_name_validation_scheme: legacy
```
- **scrape config 设置覆盖全局设置**。
- 若设置了 scrape config 的 validation 但未设置 escaping scheme,则 escaping scheme 会从 validation scheme **推断**——因此可只设 `metric_name_validation_scheme` 而无需同时指定 `metric_name_escaping_scheme`。
### 8.3 Scrape Content Negotiation for UTF-8 escaping
- 抓取时,抓取系统**必须**在 Accept header 传 `escaping=allow-utf-8` 才会被提供 UTF-8 名称;
- 若 scrape target 未见该 header,会自动用 **underscore replacement** 把 UTF-8 名称转换为 legacy-compatible。
Prometheus 及兼容抓取系统也可通过把 `escaping` header 设为不同值来请求特定 escaping 方法:
| escaping 值 | 行为 |
|:------------|:-----|
| `underscores` | **默认**;把 legacy-invalid 字符转为下划线 |
| `dots` | 类似 UnderscoreEscaping,但点转为 `_dot_`,且既有下划线转为 `__`。允许含点的简单 metric 名往返(round-trip) |
| `values` | 名称前置 `U__`,把所有非法字符替换为 unicode 值(两侧加下划线),单个下划线替换为双下划线。允许与 legacy Prometheus 之间完整往返 UTF-8 名称 |
**要点**:在 content negotiation 中声明 UTF-8 支持表示 Prometheus *能够*接收 UTF-8 字符,但**不要求** metric 名包含此前不支持的字符;Accept header 声明支持 UTF-8 也不要求 metrics 生产者禁用其侧的名称转换。确切名称转换策略由 metrics 生产者决定。要求是:**当 Prometheus 请求 allow-utf-8 之外的 escaping scheme 时,生产者必须按请求方式转换名称。**
### 8.4 Remote Write 2.0
在 Prometheus 3.0 中**自动接受**所有 UTF-8 名称;**无法**在 Remote Write 2.0 中强制 legacy 字符集校验。
### 8.5 OTLP Metrics
Prometheus 3.0 的 OTLP receiver **默认仍把所有名称规范化为 Prometheus 格式**。可在配置的 `otlp` 段更改:
```text
otlp:
# Ingest OTLP data keeping UTF-8 characters in metric/label names.
translation_strategy: NoTranslation
```
> **注意**:当不追加 type 与 unit 后缀时,若存在两个同名但 type 或 unit 不同的 metrics,它们在 Prometheus 中会**冲突(collide)**。等 Prometheus 原生支持 type 与 unit metadata 后该问题将消失。详见 OpenTelemetry guide。
### 8.6 引用语法
查询 UTF-8 名称的 metrics 在 PromQL 中需**略不同的语法**:
- classic 查询语法对 legacy-compatible 名称仍有效:`my_metric{}`;
- 但 UTF-8 名称**必须加引号并移入花括号内**:`{"my.metric"}`;
- label 名若含 legacy-incompatible 字符也必须加引号:`{"metric.name", "my.label.name"="bar"}`;
- metric 名可出现在花括号内**任意位置**,但风格上偏好放在**首个位置**。
### 8.7 注意事项汇总
| 事项 | 结论 |
|:-----|:-----|
| 默认校验 | 3.0 起默认接受全部 UTF-8 名称 |
| 强制 legacy | 用 `metric_name_validation_scheme: legacy`(全局或 scrape config,后者优先) |
| escaping scheme 推断 | 只设 validation 即可,escaping 会据此推断 |
| 抓取协商 | 必须 `escaping=allow-utf-8`,否则自动 underscore 转换 |
| Remote Write 2.0 | 无法强制 legacy |
| OTLP | 默认规范化;`translation_strategy: NoTranslation` 保留 UTF-8;注意 type/unit 冲突 |
| PromQL 引用 | `{"metric.name"}`、`{"metric.name", "label.name"="v"}` |
---
## 附录 本篇覆盖清单
> 本篇对应的官网 URL → 手册章节对照表。覆盖依据见《Prometheus-覆盖映射表.md》。
| # | 官网页面 URL | 归属章节 |
|:--:|:-------------|:---------|
| 19 | <https://prometheus.io/docs/prometheus/latest/querying/basics/> | 第 1 章 PromQL 基础 |
| 20 | <https://prometheus.io/docs/prometheus/latest/querying/examples/> | 第 4 章 查询示例集 |
| 21 | <https://prometheus.io/docs/prometheus/latest/querying/index/> | 第 1 章(导航页,见 4.3.8 说明) |
| 22 | <https://prometheus.io/docs/prometheus/latest/querying/operators/> | 第 2 章 运算符 |
| 23 | <https://prometheus.io/docs/prometheus/latest/querying/functions/> | 第 3 章 函数全表 |
| 24 | <https://prometheus.io/docs/prometheus/latest/querying/remote_read_api/> | 第 5.11 节 Remote Read API |
| 25 | <https://prometheus.io/docs/prometheus/latest/querying/api/> | 第 5 章 HTTP API |
| 37 | <https://prometheus.io/docs/prometheus/latest/management_api/> | 第 5.8 节 TSDB 管理 API |
| 70 | <https://prometheus.io/docs/guides/query-log/> | 第 6 章 查询调试 |
| 72 | <https://prometheus.io/docs/guides/utf8/> | 第 8 章 UTF-8 指标名 |
| 92 | <https://prometheus.io/docs/practices/histograms/> | 第 7 章 直方图与分位数 |
**本篇收录统计**:
- 章节:8 章 + 附录;
- 函数:**90 个**(其中实验性 16 个);
- 运算符:一元 1 个、算术 6 个、三角 1 个、直方图裁剪 2 个、比较 6 个、逻辑/集合 3 个、聚合 14 个、fill modifiers 3 个;
- HTTP API 端点:**40+ 个**(含实验性与需 flag 启用的端点);
- 查询示例:官方 examples 全部 15 条场景 + 索引页说明。
> 💡 补充:本篇所有内容均取自 `prometheus/docs` 官方仓库源文件(`raw/core/querying/*`、`raw/docs/guides/query-log.md`、`raw/docs/guides/utf8.md`、`raw/docs/practices/histograms.md`),与官网 latest 一致。凡标 `> 💡 补充:` 的段落为结合上下文的经验性说明,非官方原文。