2. 指标类型详解:Counter、Gauge、Histogram、Summary 的定义与适用场景

Prometheus 的指标模型是构建可观测性体系的基石。理解四种核心指标类型——Counter、Gauge、Histogram 和 Summary——的定义、数据特征与适用场景,是设计高效采集规则、避免数据失真、优化存储与查询性能的前提。

2.1 四种指标类型总览

下表从核心特征、数据行为与典型用途三个维度,对四种指标类型进行横向对比:

指标类型 核心特征 数据行为 典型用途
Counter 只增不减(除重启归零) 单调递增的累计值 请求总数、错误次数、任务完成数
Gauge 可增可减,反映当前快照 任意上下波动 CPU 使用率、内存占用量、并发连接数
Histogram 分桶计数 + 总和 + 总次数 观测值落入预定义桶的累计计数 请求延迟分布、响应大小分布
Summary 分位数计算 + 总和 + 总次数 在客户端侧计算滑动时间窗口内的分位数 延迟 P50/P90/P99、SLO 合规性监控
选型提示: 优先使用 Counter 和 Gauge 覆盖 80% 的基础监控场景;当需要分析数据分布或计算分位数时,再引入 Histogram 或 Summary。避免在不需要分位数的场景下滥用后两种类型,以降低存储开销。

2.2 Counter:单调递增的累计器

定义: Counter 是一个只能递增(或重启时归零)的累计值指标。它代表一个随时间单调增长的计数,例如 HTTP 请求总数、系统启动后的 CPU 时间片数。

关键行为:

  • 只支持 inc() 和 add(float) 操作,不允许减少。
  • 进程重启后,Counter 值从 0 重新开始累计。
  • 在 PromQL 中通常配合 rate() 或 increase() 函数使用,计算每秒增长率或一段时间内的增量。

适用场景:

  • 服务请求总量(http_requests_total)
  • 错误日志条数(error_log_count_total)
  • 任务队列已处理条目数(jobs_processed_total)
  • 网络字节接收总量(network_receive_bytes_total)
// 示例:Go 客户端定义 Counter
httpRequestsTotal := prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total number of HTTP requests.",
    },
    []string{"method", "endpoint"},
)
// 采集时调用
httpRequestsTotal.WithLabelValues("GET", "/api/v1/users").Inc()
避坑: 不要将 Counter 用于瞬时值(如当前内存使用量),也不要直接对 Counter 原始值做告警(如 http_requests_total > 1000),因为重启后值会归零,导致误报。应始终使用 rate() 或 increase() 包裹。

2.3 Gauge:可上可下的快照值

定义: Gauge 是一个可以任意增减的指标,用于表示某个时刻的瞬时快照值。它不关心历史趋势,只反映当前状态。

关键行为:

  • 支持 set(float)、inc()、dec()、add(float)、sub(float) 等操作。
  • 数据可以上升也可以下降,没有单调性约束。
  • 在 PromQL 中通常直接使用原始值,或配合 avg_over_time()、max_over_time() 等聚合函数。

适用场景:

  • 系统资源指标:CPU 使用率、内存使用量、磁盘剩余空间
  • 连接池状态:当前活跃连接数、空闲连接数
  • 业务指标:队列当前深度、在线用户数、缓存命中率(瞬时值)
  • 温度、湿度等传感器读数
// 示例:Python 客户端定义 Gauge
from prometheus_client import Gauge
cpu_usage = Gauge('cpu_usage_percent', 'Current CPU usage in percent')
# 采集时调用
cpu_usage.set(45.2)
最佳实践: Gauge 的采集频率通常需要高于 Counter,因为瞬时值变化快,低频采集容易丢失峰值。对于关键资源指标,建议采集间隔 ≤ 15 秒。

2.4 Histogram:分布统计的分桶利器

定义: Histogram 将观测值(如请求延迟、响应大小)分配到预定义的桶(bucket)中,并同时记录所有观测值的总和与总次数。它不直接计算分位数,而是提供桶计数供服务端计算。

关键行为:

  • 每个桶是一个 Counter,记录落入该桶及以下范围的观测值数量。
  • 自动生成 _bucket{le="..."}、_sum、_count 三个时间序列。
  • 桶边界由用户定义,通常采用指数分布(如 1ms、5ms、10ms、50ms、100ms、500ms、1s、+Inf)。
  • 服务端通过 histogram_quantile() 函数计算分位数。

适用场景:

  • 请求延迟分布分析(P50、P90、P99)
  • 响应数据包大小分布
  • 数据库查询耗时分布
  • 批处理任务执行时间分布
// 示例:Java 客户端(Micrometer)定义 Histogram
Timer.builder("api.request.duration")
    .publishPercentileHistogram()
    .sla(Duration.ofMillis(10), Duration.ofMillis(50),
         Duration.ofMillis(100), Duration.ofMillis(500))
    .register(meterRegistry);
存储成本提示: Histogram 的桶数量直接影响存储开销。每个桶对应一个独立的时间序列,建议桶数控制在 10~15 个以内。过多的桶会导致采集和存储成本线性增长。

2.5 Summary:客户端预计算的分位数

定义: Summary 在客户端侧直接计算滑动时间窗口内的分位数,并将计算结果暴露给 Prometheus 服务端。它同样记录总和与总次数,但分位数由客户端预聚合。

关键行为:

  • 需要指定目标分位数(如 0.5、0.9、0.99)和滑动窗口大小。
  • 自动生成 _quantile{quantile="..."}、_sum、_count 三个时间序列。
  • 分位数在客户端内存中计算,服务端无法重新聚合或跨进程合并。
  • 滑动窗口通常基于时间(如 5 分钟)或观测值数量。

适用场景:

  • 已知需要固定分位数且不需要跨实例聚合的场景
  • 延迟敏感型应用的 SLO 监控(如 P99 必须 ≤ 200ms)
  • 客户端资源充足,可以承担分位数计算开销
// 示例:Go 客户端定义 Summary
requestDuration := prometheus.NewSummaryVec(
    prometheus.SummaryOpts{
        Name:       "http_request_duration_seconds",
        Help:       "HTTP request duration in seconds.",
        Objectives: map[float64]float64{
            0.5:  0.05,   // P50,允许 5% 误差
            0.9:  0.01,   // P90,允许 1% 误差
            0.99: 0.001,  // P99,允许 0.1% 误差
        },
        MaxAge:     5 * time.Minute, // 滑动窗口 5 分钟
        AgeBuckets: 5,
    },
    []string{"method"},
)

2.6 Histogram vs Summary:核心差异对比

对比维度 Histogram Summary
分位数计算位置 服务端(Prometheus) 客户端(应用进程)
分位数可聚合性 支持跨实例聚合(通过 histogram_quantile) 不支持跨实例聚合
分位数精度 取决于桶边界设计,桶越密精度越高 由客户端算法和误差参数控制
存储开销 桶数 × 时间序列,桶越多开销越大 固定为分位数个数 + 2 个时间序列
客户端计算开销 低(仅桶计数) 高(需要维护排序数据结构)
适用场景 需要灵活分位数、需要跨服务聚合 固定分位数、无需聚合、客户端资源充足
选型建议: 在大多数微服务架构中,优先选择 Histogram。它允许 SRE 团队在服务端灵活调整分位数计算方式,且支持跨实例聚合,更适合全局视角的延迟分析。Summary 仅在客户端分位数计算有特殊精度要求或无法承受桶存储开销时使用。

2.7 指标类型选择决策流程

指标类型选择决策流程 需要监控什么数据? 数据是否只增不减? Counter 需要分析分布或分位数? Gauge 需要跨实例聚合分位数? Histogram Summary 说明: • 80% 场景使用 Counter 和 Gauge 即可满足需求。 • 需要分布分析时,优先选择 Histogram,仅在特殊精度要求下使用 Summary。

2.8 采集优化要点

针对不同指标类型,在采集与服务目录数据传输层面有以下优化建议:

  • Counter 去重: 由于 Counter 单调递增,采集端可以只传输增量(差值),减少每次上报的数据量。服务端保留基准值即可。
  • Gauge 压缩: Gauge 值变化频繁,建议在采集端使用有损压缩(如死区压缩),仅当值变化超过阈值时才上报,降低传输带宽。
  • Histogram 桶裁剪: 删除长期为零的桶(如高延迟桶在低负载时无数据),减少无意义的时间序列传输。
  • Summary 窗口对齐: 确保 Summary 的滑动窗口与采集周期对齐,避免因窗口错位导致分位数抖动,进而引发不必要的告警风暴。
服务目录数据优化: 在服务目录中为每个指标类型标注 metric_type 字段,使采集器可以自动选择对应的优化策略(如 Counter 增量传输、Gauge 死区压缩),实现采集链路的自适应调优。