2. OpenTelemetry 指标体系回顾:Counter、Gauge、Histogram、Exponential Histogram 的核心区别

在深入降采样与成本控制之前,必须彻底理解 OpenTelemetry 定义的四种核心指标类型。它们的数据结构、聚合方式与语义直接决定了后续的存储策略与采样精度。错误的类型选择会导致数据失真或存储浪费。

2.1 四种指标类型总览

指标类型 语义 数据方向 典型用例 降采样难度
Counter 单调递增计数器 只增不减(重启可重置) 请求总数、错误总数、CPU 时间 低(速率聚合即可)
Gauge 瞬时值快照 可增可减 内存使用量、队列深度、温度 中(需保留极值或分位数)
Histogram 预定义桶的分布统计 累积计数 + 总和 请求延迟、响应大小 高(桶边界固定,合并困难)
Exponential Histogram 指数桶的分布统计 累积计数 + 总和 + 最小值/最大值 高动态范围延迟、长尾分布 中(可合并,但需控制精度)
关键认知: 降采样成本控制的本质是对不同指标类型采用差异化的聚合策略。Counter 可以安全地降采样为速率,而 Histogram 的降采样几乎必然引入误差。

2.2 Counter:单调递增计数器

Counter 是最简单的指标类型,只支持 Add() 操作,值只能增加(系统重启时允许归零)。在 OpenTelemetry 中,Counter 的数据点包含时间戳、值和属性集。

// OpenTelemetry Go SDK 示例
counter, _ := meter.Int64Counter("http.requests.total")
counter.Add(ctx, 1, attribute.String("method", "GET"))
counter.Add(ctx, 1, attribute.String("method", "POST"))

降采样策略: 将原始 Counter 数据点转换为 rate() 或 delta 值,丢弃原始累积值。例如,每 10 秒的原始点可以聚合为每分钟的速率,存储量减少 83%。

成本优化要点: Counter 的原始累积值在长期存储中几乎没有价值,因为查询时总是需要计算 rate()。建议在采集端直接发送 delta 格式,或由代理层做速率转换。

2.3 Gauge:瞬时值快照

Gauge 表示可上可下的瞬时测量值。每个数据点独立,没有累积语义。典型场景包括内存使用率、连接数、队列长度。

// OpenTelemetry Go SDK 示例
gauge, _ := meter.Int64ObservableGauge("system.memory.usage")
gauge.RegisterCallback(func(ctx context.Context, observer metric.Int64Observer) error {
    observer.Observe(getMemoryUsage(), attribute.String("host", hostname))
    return nil
})

降采样策略: 对 Gauge 降采样时,必须保留 最小值、最大值、平均值 或 最后值。仅保留平均值会丢失峰值信息,导致容量规划失真。

降采样方法 保留信息 适用场景 存储节省
Last Value 最后一个采样点 变化缓慢的指标(温度) ~90%
Min/Max/Avg 三个聚合值 资源使用率(CPU、内存) ~70%
Percentile Sketch 近似分位数 需要 P99 的 Gauge ~50%

2.4 Histogram:预定义桶分布

Histogram 将观测值分配到预定义的桶边界中,同时记录总和与计数。这是 OpenTelemetry 中最复杂、存储成本最高的指标类型。

// 典型的桶边界配置
histogram, _ := meter.Float64Histogram("http.request.duration",
    metric.WithExplicitBucketBoundaries(
        0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
    ),
)
histogram.Record(ctx, 0.042) // 落入 0.025~0.05 桶

降采样困境: 两个 Histogram 的桶合并(加法)在数学上是精确的,但一旦降采样到更少的桶,原始分布形状就会永久丢失。例如,将 11 个桶合并为 5 个桶后,无法还原 P99 的精确值。

避坑指南: 不要对 Histogram 做时间维度的简单平均降采样。例如,将 10 秒的 Histogram 数据点平均为 1 分钟的点,会严重低估长尾延迟。正确的做法是使用 HDR Histogram 或 DDSketch 等可合并的数据结构。

2.5 Exponential Histogram:指数桶分布

Exponential Histogram(指数直方图)是 OpenTelemetry 在 1.21+ 版本中引入的改进类型。它使用指数增长的桶宽度,在低值区域提供高精度,在高值区域自动合并桶。

// Exponential Histogram 配置(OTLP 协议原生支持)
// 不需要手动指定桶边界,只需设置最大桶数
// 默认 scale=20,最大桶数 160
exponentialHistogram, _ := meter.Float64Histogram("http.request.duration.exponential",
    metric.WithExponentialBucketBoundaries(160),
)

核心优势: 两个 Exponential Histogram 可以在不损失精度的情况下合并,因为它们的桶边界是对数一致的。这使得分布式聚合和降采样成为可能。

特性 Histogram(预定义桶) Exponential Histogram
桶边界 用户手动指定,固定 自动指数增长,可配置精度
合并能力 精确合并(相同桶边界) 精确合并(相同 scale)
降采样友好度 低(桶合并后精度不可逆) 高(可调整 scale 降精度)
存储开销 固定(桶数 × 属性组合) 动态(稀疏桶,低基数时更省)
P99 精度 受桶边界限制 可配置,通常优于 1%
成本优化建议: 对于延迟和响应大小等高动态范围指标,优先使用 Exponential Histogram。它可以在保持 P99 精度 < 1% 的同时,将存储量降低到传统 Histogram 的 1/3 到 1/5。

2.6 四种指标类型的选择决策树

指标类型选择决策树 指标数据 只增不减? 可增可减? Counter 需要分布? Gauge 高动态范围? Histogram Exponential Histogram 💡 决策要点:Counter 和 Gauge 存储成本低,降采样简单;Histogram 成本高且降采样困难; Exponential Histogram 是延迟类指标的推荐选择,兼顾精度与存储效率。

2.7 降采样视角下的关键差异总结

  • Counter:降采样最安全,转换为 rate() 后几乎无信息损失,存储量可降低 80-90%。
  • Gauge:降采样必须保留极值信息,建议使用 Min/Max/Avg 三元组或 T-Digest 草图。
  • Histogram:降采样风险最高,桶合并后无法恢复原始分布,建议保留原始粒度或改用 Exponential Histogram。
  • Exponential Histogram:支持无损合并和精度可调的降采样,是成本控制的最佳实践选择。
实战建议: 在 OpenTelemetry Collector 中配置 metrics_transform 处理器,将低价值的 Histogram 转换为 Exponential Histogram,并设置 max_buckets=80 以平衡精度与存储。对于不需要分位数的指标,直接降采样为 Counter 或 Gauge。