指标(Metrics):Counter、Gauge、Histogram、Summary

聊可观测性,绕不开四种核心指标类型。它们就像监控世界的四种基本颜色,混搭才能调出真实的系统画面。我个人习惯把这四种类型当成「四种视角的探针」,每种看到的系统侧面都不一样。

Counter、Gauge、Histogram、Summary —— 这四个名词你肯定见过。但什么时候用哪个?各有什么坑?这才是关键。先看一张总览图,把全貌装进脑子里,我们再逐个拆。

四种核心指标类型 Counter(计数器) 只增不减,统计请求数、错误数 Gauge(仪表盘) 可增可减,反映当前状态 Histogram(直方图) 值分布统计,计算百分位数 Summary(摘要) 预计算分位数,客户端算好 核心选择逻辑 • 只关心总量或速率? → Counter • 需要当前快照值? → Gauge • 想看延迟分布和百分位?先看内存开销:小用 Histogram,客户端可控用 Summary • 需要精确的预定义分位数?用 Summary(但无法后续聚合)
个人经验: 刚开始做监控时,我什么都用 Counter 或 Gauge,结果查延迟问题得从原始日志里慢慢扒,非常痛苦。后来用了 Histogram,才发现百分位数的威力——短短一行指标就能告诉你「99% 的请求都在 200ms 以内」,比平均值靠谱多了。

Counter(计数器)—— 只增不减的累计值

Counter 是最朴素的指标类型。它只能加,不能减。重启时归零,然后重新累加。你想想看,什么场景符合这个特征?

  • HTTP 请求总数
  • 数据库查询次数
  • 队列投递的消息量
  • 业务异常发生次数

Counter 本身其实是个单调递增的数字,但光看值往往没有意义。几千次请求 vs 几万次请求,脱离了时间窗口很难判断。所以我们通常配合 rate() 或 irate() 函数计算速率,比如 rate(http_requests_total[5m]) 就能知道最近 5 分钟平均每秒处理的请求数。

要点: Counter 不能用于「当前连接数」「当前线程数」这类可增可减的指标。有人用 Counter 记线程数,结果线程释放后 Counter 不降,监控数据完全错了。这是非常典型的误用。
// Prometheus 客户端示例(Go)
counter := prometheus.NewCounter(prometheus.CounterOpts{
    Name: "myapp_requests_total",
    Help: "Total number of requests processed.",
})
counter.Inc()        // 加 1
counter.Add(5)       // 加 5
我曾经踩过的坑: 用 Counter 的时候,注意区分「成功」和「失败」的计数。一个常见的错误是只统计总请求数,不按状态码拆分。结果告警说错误率上升,却分不清是哪个接口出了问题。所以记得给 Counter 打上标签(label),比如 method、status、endpoint。

Gauge(仪表盘)—— 系统的即时快照

Gauge 和 Counter 正好相反,它允许数值上下波动。它反映的是「这一刻」的状态,而不是累计历史。

适合的场景很明显:

  • 当前 CPU 使用率
  • 内存使用量
  • 活跃连接数
  • 队列当前深度
  • 磁盘剩余空间

Gauge 没有速率的概念,它的价值在于快照和趋势。监控面板上常见的实时曲线图,大多由 Gauge 驱动。需要注意的是,Gauge 的值通常由程序主动上报,如果上报间隔过长,曲线会变得很毛糙,建议配合合理的拉取频率(比如 10-30 秒一次)。

// Prometheus 客户端示例(Java)
Gauge gauge = Gauge.build()
    .name("myapp_memory_used_bytes")
    .help("Current memory usage in bytes.")
    .register();
gauge.set(1024 * 1024 * 256);    // 设 256MB
gauge.dec(1024 * 1024);          // 减 1MB
gauge.inc();                      // 加 1
个人习惯: 用 Gauge 监控资源水位时,我会加上上限告警。比如内存超过 80% 发警告,超过 95% 发严重。但小心 Gauge 的「尖刺」——一瞬间的波动可能只是因为 GC 或突发 IO,建议在告警规则里加一个小时间窗口(比如持续 30 秒),避免误报。

Histogram(直方图)—— 统计分布的秘密武器

Histogram 解决了一个很实际的问题:光知道平均值没用,得知道数据是怎么分布的。你想想看,99% 的请求都在 100ms 以内,但 1% 的请求耗时 10 秒,平均值可能还是 200ms,漂亮的平均值掩盖了严重的毛刺。

Histogram 在工作时会把你传入的观测值分配到预定义的桶(bucket)里。比如定义桶为:[0.1, 0.5, 1, 2.5, 5, 10] 秒,每次请求耗时 0.8 秒,就丢进 <=1 秒的那个桶。最终你会得到每个桶里的计数,然后可以用公式算出百分位数(P99、P95 等)。

桶区间累计计数
≤ 0.1s1500
≤ 0.5s8500
≤ 1s9800
≤ 2.5s9970
≤ 5s9990
≤ 10s10000

从这张表里可以算出:P99 大约是 2.5s(因为 99% 的请求都在 <=2.5s 桶内)。P50 则在 0.5s 到 1s 之间,因为 85% 在 0.5s 以内,98% 在 1s 以内,所以中位数大约在 0.6s 左右。

// Prometheus 客户端示例
histogram := prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    "myapp_request_duration_seconds",
    Help:    "Request latency in seconds.",
    Buckets: prometheus.DefBuckets,   // 默认桶:[.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]
})
histogram.Observe(0.8)
避坑指南: 桶的定义直接影响准确度。桶太宽,百分位估算误差大;桶太窄,指标数量暴增(每个桶就是一个时间序列)。我建议桶的数量控制在 8-15 个,重点关注目标百分位附近的桶密度。比如你的 SLO 是 P99 < 1s,那就在 1s 附近多布置几个精确桶。

Summary(摘要)—— 客户端预计算的分位数

Summary 和 Histogram 有点像,都是为了算分位数。但区别在于:Histogram 把原始分布抛给服务端去算,而 Summary 在客户端就完成了分位数的预计算,直接上报计算好的值(比如 0.5、0.9、0.99 分位数)。

这么做的好处是服务端压力小,不需要实时计算百分位。代价是你必须提前定义好想要哪些分位点(例如只想要 P50、P90、P99),且后续无法再增加新的分位点(因为原始数据已经丢了)。

特性HistogramSummary
分位数计算位置服务端(查询时聚合)客户端(上报前计算)
是否需要预定义分位点不需要需要(定义 quantile)
可在多实例间聚合可以(累计直方图)不可以(无法聚合预计算值)
内存开销与桶数量成正比与流式算法实现有关,通常可控
适用场景需要灵活查询任意百分位已经知道明确的 SLO 分位点
// Prometheus 客户端示例(Go)
summary := prometheus.NewSummary(prometheus.SummaryOpts{
    Name:       "myapp_request_duration_seconds",
    Help:       "Request latency in seconds.",
    Objectives: map[float64]float64{
        0.5: 0.05,   // 允许 5% 误差
        0.9: 0.01,   // 允许 1% 误差
        0.99: 0.001, // 允许 0.1% 误差
    },
})
summary.Observe(0.8)
我如何选择: 如果团队需要灵活的告警和调优(比如随时查看 P95、P99.9),我会优先用 Histogram。如果已经明确了 SLO 上的固定分位点,且对服务端性能敏感,可以考虑 Summary。但说实话,现在的 Prometheus 和 Thanos 对 Histogram 的支持已经很成熟,一般场景用 Histogram 更泛用。

选型速查表

需求推荐指标类型原因
统计每秒请求数Counter + rate()只增不减,速率计算天然适合
当前内存使用量Gauge可增可减,记录瞬时状态
请求延迟分布、P99Histogram灵活,可聚合,桶可调
已确定 SLO 的延迟分位数Summary客户端计算,服务端压力小
业务非负累加值(订单量、访问量)Counter天然累计语义
队列大小、连接池占用Gauge随时变化,快照数据
数据库查询耗时分布Histogram需要多维度分析慢查询

嗯,四种指标到这里就聊完了。记住一句话:没有银弹,每种指标都有自己的性格。Counter 老实巴交只管累加,Gauge 反应灵敏但容易受毛刺干扰,Histogram 信息量大但桶的设计有讲究,Summary 省服务端但灵活性差。选对工具,监控工作至少轻松一半。我在项目中碰到的绝大多数告警漂移问题,追到底都是指标类型用错了。所以,每次加新指标之前,先问自己一句:这个指标到底是什么语义?该用 Counter 还是 Gauge?该用 Histogram 还是 Summary?答案自然就出来了。