2. OpenTelemetry 指标体系概述

在深入探讨聚合粒度对 SLI 精度的影响之前,必须先理解 OpenTelemetry (OTel) 的指标体系是如何构建的。OTel 提供了一套标准化的 API 和 SDK,用于生成、处理和导出指标数据。其核心设计围绕 Meter、Instrument 和 数据模型 展开,确保跨语言、跨后端的可移植性。

2.1 核心概念:Meter 与 MeterProvider

Meter 是创建指标 Instrument 的工厂。每个 Meter 由 MeterProvider 管理,通常一个应用程序只有一个 MeterProvider 实例。Meter 负责记录指标的名称、版本和 Schema URL,并作为 Instrument 的命名空间。

关键点: MeterProvider 是全局入口点,负责配置导出器(Exporter)和聚合策略。所有指标数据最终都通过 MeterProvider 注册的 Reader 导出。
OTel 指标体系核心架构 MeterProvider Meter Counter Histogram UpDownCounter 数据模型:MetricPoint + Attributes + Time

2.2 三种核心 Instrument

OTel 定义了多种 Instrument 类型,其中 Counter、Histogram 和 UpDownCounter 是最常用的三种。它们各自适用于不同的 SLI 场景。

Instrument 语义 典型 SLI 场景 聚合行为
Counter 单调递增,只增不减 请求总数、错误总数、延迟累计 Delta 或 Cumulative 求和
Histogram 记录值的分布,支持分位数 请求延迟、响应大小 分桶计数 + 总和 + 最小值/最大值
UpDownCounter 可增可减,不单调 活跃连接数、队列深度 Delta 或 Cumulative 求和
选择建议: 对于延迟类 SLI(如 p99 延迟),必须使用 Histogram;对于计数类 SLI(如错误率),使用 Counter;对于瞬时量类 SLI(如并发数),使用 UpDownCounter。

2.3 Counter 详解

Counter 是最简单的 Instrument,只支持 Add() 操作,不允许减少。它通常用于累计事件发生的次数。在 OTel 中,Counter 可以导出为 Delta(两次采集之间的增量)或 Cumulative(从进程启动开始的累计值)。

// 伪代码示例:创建并记录 Counter
meter = meterProvider.getMeter("my-service")
requestCounter = meter.createCounter("http.requests.total", 
    description: "Total number of HTTP requests")
requestCounter.add(1, attributes: {method: "GET", status: "200"})

2.4 Histogram 详解

Histogram 是延迟类 SLI 的核心。它记录值的分布,而不是单个值。OTel 的 Histogram 默认使用 Exponential Histogram 或 Explicit Bucket Histogram。Exponential Histogram 在保持高精度的同时,能自动适应数据范围,减少配置负担。

聚合粒度陷阱: Histogram 的桶边界(Bucket Boundaries)直接影响分位数计算的精度。如果桶太宽,p99 可能被低估或高估;如果桶太窄,会增加存储和计算开销。这是后续章节重点讨论的内容。
// 伪代码示例:创建并记录 Histogram
latencyHistogram = meter.createHistogram("http.request.duration",
    unit: "ms",
    description: "HTTP request latency in milliseconds")
latencyHistogram.record(120.5, attributes: {route: "/api/users"})

2.5 UpDownCounter 详解

UpDownCounter 支持 Add() 正数和负数,适合跟踪可增减的指标。例如,活跃连接数、内存使用量、队列长度等。它不要求单调性,因此可以反映系统的瞬时状态。

// 伪代码示例:创建并记录 UpDownCounter
activeConnections = meter.createUpDownCounter("db.connections.active",
    description: "Number of active database connections")
activeConnections.add(1)  // 连接建立
activeConnections.add(-1) // 连接关闭

2.6 数据模型:MetricPoint 与 Attributes

所有 Instrument 最终都生成 MetricPoint 数据点。一个 MetricPoint 包含以下核心字段:

  • Name:指标名称,如 http.request.duration
  • Attributes:键值对标签,用于维度拆分(如 method、status_code)
  • Time:时间戳,表示数据点采集的时间
  • Value:具体数值(Counter 为累计值,Histogram 为桶计数)
  • Exemplar:可选,关联到具体 Trace 的样本值

Attributes 是聚合粒度的关键。例如,如果只按 service 聚合,则无法区分不同 API 路由的延迟;如果按 route 和 method 聚合,则维度爆炸可能导致存储成本激增。

MetricPoint 数据模型结构 MetricPoint Name: "http.request.duration" Attributes: {method, route, status} Time: 2025-01-15T10:30:00Z Value: Histogram Bucket Counts

2.7 聚合与导出

OTel SDK 在内存中完成初步聚合,然后通过 MetricReader 定期导出。常见的导出器包括 Prometheus Exporter、OTLP Exporter 等。聚合粒度由以下因素决定:

  1. Attributes 基数:每个唯一 Attributes 组合形成一个时间序列
  2. 聚合周期:导出间隔(如 15s、60s)
  3. Histogram 桶数:桶越多,精度越高,但存储越大
  4. 聚合类型:Delta vs Cumulative 影响查询时的计算方式
实践要点: 在定义 SLI 时,需要明确 Attributes 的维度。例如,对于“全局错误率”,只需 service 标签;对于“按路由的错误率”,需要 route 标签。维度越细,SLI 精度越高,但成本也越高。