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 四种指标类型的选择决策树
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。