3. 核心数据模型:Metric(指标)的定义、数据类型
在可观测性体系中,Metric(指标) 是描述系统状态随时间变化的数值化度量。与 Log 的离散事件和 Trace 的请求链路不同,Metric 通过聚合统计的方式,以较低的存储成本提供系统健康度、性能趋势和异常检测能力。OpenTelemetry 将 Metric 数据模型标准化,定义了四种基础数据类型,覆盖从单调递增计数到可增减状态值的全部场景。
3.1 Metric 的定义与核心特征
Metric 是对某一可观测对象(如 CPU 使用率、HTTP 请求数、队列深度)在时间维度上的数值采样。其核心特征包括:
- 时间序列:每个 Metric 由一组带时间戳的数值点构成,按时间顺序排列。
- 维度(Attributes):通过键值对(如
method="GET"、status="200")对指标进行细分,形成多维数据模型。 - 聚合语义:指标在采集端或后端进行聚合(如 Sum、Count、Min/Max/Sum),减少数据量并提取统计特征。
- 低开销:相比 Log 和 Trace,Metric 的采集和传输开销更低,适合高频采样。
3.2 四种核心数据类型
OpenTelemetry 定义了四种基础 Metric 数据类型,每种类型对应不同的语义和适用场景。下表总结了它们的核心差异:
| 数据类型 | 语义 | 是否可增减 | 典型场景 | 聚合方式 |
|---|---|---|---|---|
| Counter | 单调递增计数器 | 仅增加 | 请求总数、错误次数、字节发送量 | Sum |
| UpDownCounter | 可增减计数器 | 可增可减 | 活跃连接数、队列长度、库存量 | Sum |
| Histogram | 数值分布统计 | 仅增加(采样点) | 请求延迟、响应大小、批处理耗时 | Count、Sum、Min/Max、分位数 |
| Gauge | 瞬时值快照 | 可增可减(瞬时) | CPU 使用率、内存占用、温度 | LastValue |
3.3 数据类型详解
3.3.1 Counter(单调递增计数器)
Counter 是最常用的指标类型,其值只能增加,不能减少。它适用于记录累计总量,如 HTTP 请求总数、数据库查询次数、错误发生次数。Counter 的典型特征包括:
- 单调性:每次调用
Add()方法时,值增加一个正数;不支持减法操作。 - 聚合语义:后端通常对同一时间窗口内的 Counter 值进行 Sum 聚合,得到总累计值。
- 重置处理:当进程重启时,Counter 值会从 0 重新开始;后端通过速率计算(如 per-second rate)来消除重置影响。
// 伪代码示例:创建并增加 Counter
Counter httpRequests = meter.createCounter("http.requests.total");
httpRequests.add(1, Attributes.of("method", "GET", "status", "200"));
3.3.2 UpDownCounter(可增减计数器)
UpDownCounter 允许值增加或减少,适用于跟踪可以上下波动的累计量。与 Counter 不同,它不要求单调性,但同样基于 Sum 聚合。典型场景包括:
- 活跃连接数:新连接建立时 +1,连接关闭时 -1。
- 队列深度:任务入队时 +1,出队时 -1。
- 并发请求数:请求开始 +1,请求结束 -1。
// 伪代码示例:创建并操作 UpDownCounter
UpDownCounter activeConnections = meter.createUpDownCounter("connections.active");
activeConnections.add(1); // 新连接
activeConnections.add(-1); // 连接关闭
3.3.3 Histogram(直方图)
Histogram 用于记录数值的分布情况,而不是简单的总和或计数。它通过将数值分配到预定义的桶(Bucket)中,统计每个桶内的样本数量,从而提供分位数、平均值、最小值、最大值等统计信息。Histogram 的核心特性:
- 桶边界:用户可自定义桶边界(如 [0, 10, 50, 100, 500]),或使用默认指数边界。
- 聚合输出:后端可计算 Count(样本总数)、Sum(数值总和)、Min/Max、以及分位数(如 p50、p95、p99)。
- 适用场景:请求延迟、响应大小、批处理耗时等需要了解分布特征的指标。
// 伪代码示例:创建 Histogram 并记录值
Histogram requestLatency = meter.createHistogram("http.request.duration");
requestLatency.record(120.5, Attributes.of("method", "POST"));
3.3.4 Gauge(瞬时值)
Gauge 用于记录某一时刻的瞬时值,不进行累加或聚合。它适合表示当前状态,如 CPU 使用率、内存占用、磁盘空间、温度等。Gauge 的关键特征:
- 瞬时性:每次记录的值代表该时刻的快照,不保留历史累计信息。
- 聚合方式:后端通常使用 LastValue 聚合,即取最后一个上报的值作为当前状态。
- 回调模式:OpenTelemetry 支持通过回调函数异步获取 Gauge 值,避免高频轮询。
// 伪代码示例:创建 Gauge 并设置回调
meter.createGauge("system.cpu.usage", () -> getCurrentCpuUsage());
3.4 数据类型选择决策流程
在实际开发中,选择正确的 Metric 类型是保证数据语义准确的关键。以下决策流程可帮助快速判断:
- 是否只关心累计总量? → 是 → Counter(仅增加)或 UpDownCounter(可增减)。
- 是否需要了解数值分布? → 是 → Histogram(如延迟、大小)。
- 是否只关心当前瞬时值? → 是 → Gauge(如 CPU、内存)。
- 是否需要在进程重启后保持语义? → Counter 和 Histogram 支持速率计算;Gauge 和 UpDownCounter 需注意重置影响。
3.5 Metric 数据模型与四种类型的关系
下图展示了 OpenTelemetry Metric 数据模型的核心结构,以及四种数据类型在采集、聚合、导出流程中的位置:
3.6 数据类型对比与选型建议
下表从多个维度对比四种数据类型,帮助开发者在实际场景中快速选型:
| 维度 | Counter | UpDownCounter | Histogram | Gauge |
|---|---|---|---|---|
| 值变化方向 | 仅增加 | 可增可减 | 仅增加(采样点) | 可增可减(瞬时) |
| 聚合方式 | Sum | Sum | Count、Sum、Min/Max、分位数 | LastValue |
| 典型输出 | 总请求数、总错误数 | 活跃连接数、队列深度 | p50/p95/p99 延迟、平均大小 | CPU 使用率、内存占用 |
| 重置处理 | 支持速率计算 | 需注意重置影响 | 支持速率计算 | 无需处理(瞬时值) |
| 推荐使用频率 | 高 | 中 | 高 | 中 |
3.7 常见误区与注意事项
- 误区一:用 Gauge 记录累计值。Gauge 的 LastValue 聚合会丢失历史累计信息,应使用 Counter 或 UpDownCounter。
- 误区二:用 Counter 记录可增减值。Counter 不支持减法,会导致数据语义错误;应使用 UpDownCounter。
- 误区三:忽略 Histogram 的桶配置。默认桶边界可能不适合业务场景,需根据实际数据分布自定义桶。
- 误区四:高频更新 Gauge。Gauge 的瞬时性意味着高频更新不会带来额外价值,反而增加开销;建议使用回调模式或降低采样频率。