# queryName 和 timeAggregation 字段的语义说明

## 问题描述

在健康检查 API 的返回结果中，资源类指标（CPU、内存）存在命名歧义：

```json
{
  "queryName": "TiDB_CPU_P80",
  "timeAggregation": "P90",
  "threshold": "7.6%",
  "max": "0.00",
  "avg": "0.00"
}
```

**用户困惑点**：
- queryName 包含 "P80"，timeAggregation 是 "P90"
- 这是 P80 还是 P90？
- 两个百分位数是什么关系？

## 语义解释

这里涉及**两个维度**的百分位数概念：

### 维度1：queryName 中的 P80/P90（空间维度/实例维度）

**含义**：在**某一时刻**，对**多个实例**的指标值进行百分位数计算。

**具体案例**：监控 TiDB CPU 使用率

假设集群中有 10 个 TiDB 实例，在 **2026-02-08 10:00:00** 这一时刻，它们的 CPU 使用率分别是：

```
实例1:  15%
实例2:  22%
实例3:  35%
实例4:  42%
实例5:  55%
实例6:  61%
实例7:  73%
实例8:  82%  ← P80 位置（80%的实例 ≤ 这个值）
实例9:  89%  ← P90 位置（90%的实例 ≤ 这个值）
实例10: 95%
```

- **TiDB_CPU_P80** 查询：得到 **82%**（80% 的实例的 CPU 不超过这个值）
- **TiDB_CPU_P90** 查询：得到 **89%**（90% 的实例的 CPU 不超过这个值）

### 维度2：timeAggregation 的 P90/AVG（时间维度）

**含义**：在**一段时间内**（如一天），对**多个时间点**的指标值进行聚合计算。

**具体案例**：继续上面的 TiDB_CPU_P80

假设我们在一天内每 4 分钟采样一次，得到 360 个 TiDB_CPU_P80 的值：

```
10:00:00 → P80 = 82%
10:04:00 → P80 = 85%
10:08:00 → P80 = 78%
10:12:00 → P80 = 91%
10:16:00 → P80 = 88%
...
23:56:00 → P80 = 75%

共 360 个数据点
```

对这 360 个 P80 值进行时间维度的聚合：

- **timeAggregation = "P90"**：这 360 个 P80 值的 P90 是多少？
  - 计算结果假设是 **92%**（90% 的时间点的 P80 值不超过 92%）

- **timeAggregation = "AVG"**：这 360 个 P80 值的平均值是多少？
  - 计算结果假设是 **84%**（所有时间点的 P80 值的平均）

## 完整数据流示例

### 原始数据采集（部分时间点）

| 时刻 | 实例1 | 实例2 | ... | 实例10 | **P80值** | **P90值** |
|------|-------|-------|-----|--------|-----------|-----------|
| 10:00 | 15% | 22% | ... | 95% | **82%** | **89%** |
| 10:04 | 18% | 25% | ... | 98% | **85%** | **92%** |
| 10:08 | 12% | 20% | ... | 90% | **78%** | **85%** |
| 10:12 | 20% | 28% | ... | 99% | **91%** | **96%** |
| ... | ... | ... | ... | ... | ... | ... |
| 23:56 | 10% | 18% | ... | 88% | **75%** | **82%** |

### 时间维度聚合结果

对上面的 P80 列和 P90 列分别进行时间聚合：

| queryName | timeAggregation | 计算方式 | 结果值 |
|-----------|----------------|---------|--------|
| TiDB_CPU_P80 | P90 | P80 列的所有值 (82%, 85%, 78%, 91%, ..., 75%) 的 P90 | **92%** |
| TiDB_CPU_P80 | AVG | P80 列的所有值的平均值 | **84%** |
| TiDB_CPU_P90 | P90 | P90 列的所有值 (89%, 92%, 85%, 96%, ..., 82%) 的 P90 | **95%** |
| TiDB_CPU_P90 | AVG | P90 列的所有值的平均值 | **88%** |

## max 和 avg 字段的含义

在返回结果中，`max` 和 `avg` 字段表示**所有原始时间点数据的统计量**：

```json
{
  "queryName": "TiDB_CPU_P80",
  "timeAggregation": "P90",
  "threshold": "7.6%",        // (92% / baseline的P90) - 1 = 7.6%
  "max": "95.00",             // 所有P80值中的最大值：max([82,85,78,...,75]) = 95%
  "avg": "84.00"              // 所有P80值的平均值：mean([82,85,78,...,75]) = 84%
}
```

**重要说明**：
- `max` 和 `avg` 是基于所有原始时间点的 P80 值（360个数据点）计算的
- **不是**基于 timeAggregation 聚合后的单个值（P90=92%）
- 同一个 queryName 的两条记录（P90 和 AVG）的 max/avg 值相同，因为它们基于同一批原始数据

### 完整语义解读

```json
{
  "queryName": "TiDB_CPU_P80",
  "timeAggregation": "P90",
  "threshold": "7.6%",     // ⚠️ Primary的P90相比Baseline的P90增长了7.6%
  "max": "95.00",          // ℹ️ Primary期间所有P80值的最高点是95%
  "avg": "84.00"           // ℹ️ Primary期间所有P80值的平均是84%
}
```

**用中文总结**：
- **queryName**: `TiDB_CPU_P80` = "每个时刻各实例 CPU 的 P80 值"
- **timeAggregation**: `P90` = "对一天内所有时刻的 P80 值，计算它们的 P90"
- **完整含义**: "一天内所有时刻的（各实例 CPU P80）的 P90 聚合"

## 代码位置

### 空间维度百分位数（Prometheus 查询）

- **配置文件**: `configs/config-metrics.yaml`
- **查询模板**: 包含 `quantile(0.8, ...)` 或 `quantile(0.9, ...)` 的 PromQL 表达式

### 时间维度聚合

- **文件**: `pkg/metrics/metrics_calculate.go`
- **函数**: `getResourceDiffResult()`（第16-89行）

```go
// primaryValues 包含了所有时间点的 P80 值
// 比如：[82%, 85%, 78%, 91%, ..., 75%]

// 第 37 行：计算这些 P80 值的 P90
primaryP90, err := stats.Percentile(trimmedPrimary, 90)  // → 92%

// 第 42 行：计算这些 P80 值的平均值
primaryAvg, err := stats.Mean(trimmedPrimary)  // → 84%

// 第 61-64 行：生成 P90 聚合结果
diffResults = append(diffResults, dtoMetrics.DiffResult{
    TimeAggregation: "P90",  // ← 这是时间维度的 P90
    Diff:            diffP90,
})

// 第 71-74 行：生成 AVG 聚合结果
diffResults = append(diffResults, dtoMetrics.DiffResult{
    TimeAggregation: "AVG",  // ← 这是时间维度的 AVG
    Diff:            diffAvg,
})
```

## 改进方案（待评估）

### 方案A：去除 queryName 中的百分位数后缀

**修改后效果**：
```json
{
  "queryName": "TiDB_CPU",           // ← 去掉 _P80 后缀
  "timeAggregation": "P90",
  "threshold": "7.6%",
  "max": "95.00",
  "avg": "84.00"
}
```

**优点**：
- 语义更清晰："TiDB CPU 的 P90 聚合" 比 "TiDB CPU P80 的 P90 聚合" 更容易理解
- 减少用户困惑

**缺点**：
- 丢失了空间维度的百分位数信息（P80 vs P90）
- 前端如果硬编码了完整的 queryName 需要同步修改
- 需要合并大量冗余的 switch-case 分支和函数

### 方案B：在 queryName 中明确区分维度

**修改后效果**：
```json
{
  "queryName": "TiDB_CPU",
  "spatialPercentile": "P80",        // ← 新增：空间维度百分位数
  "timeAggregation": "P90",          // ← 时间维度聚合
  "threshold": "7.6%",
  "max": "95.00",
  "avg": "84.00"
}
```

**优点**：
- 语义最清晰，两个维度分开表示
- 保留完整信息

**缺点**：
- 需要修改数据结构（API 响应格式变化）
- 前端需要适配新字段
- 改动范围大

### 方案C：只在文档中说明（当前方案）

**优点**：
- 不需要改代码
- 保持向后兼容

**缺点**：
- 用户还是会困惑
- 治标不治本

## 总结表格

| 概念 | 维度 | 作用对象 | 举例 |
|------|------|---------|------|
| queryName 中的 P80/P90 | 空间维度（实例） | 在某一时刻，对多个实例进行百分位数计算 | 10 个实例的 CPU，取 P80 = 82% |
| timeAggregation 的 P90/AVG | 时间维度 | 在一段时间内，对多个时间点的数据进行聚合 | 360 个 P80 值，取 P90 = 92% 或 AVG = 84% |
| max 字段 | 统计量 | 所有原始时间点数据的最大值 | max([82, 85, 78, ...]) = 95% |
| avg 字段 | 统计量 | 所有原始时间点数据的平均值 | mean([82, 85, 78, ...]) = 84% |

## 决策建议

建议采用**方案A**（去除 queryName 后缀），因为：
1. 改动范围适中
2. 语义改进明显
3. 对用户更友好

但需要注意：
- 与前端团队确认是否有依赖
- 充分测试所有相关功能
- 准备好回退方案

---

**文档创建时间**: 2026-03-24
**相关问题**: 健康检查 API 指标语义歧义
**状态**: 待决策
