
在云原生和微服务架构盛行的今天,监控系统的重要性不言而喻。Prometheus 作为其中的佼佼者,凭借其强大的数据模型和查询语言,成为了监控领域的标准。
然而要真正发挥 Prometheus 的威力,首先需要深刻理解其核心——四大指标类型。这篇文章我们将深入实践,彻底搞懂 Counter、Gauge、Histogram 和 Summary,并结合实际场景,让你知道在何时何地选择合适的指标类型。准备好了吗?让我们开始这场监控之旅! 😎
想象你正在负责一个电商订单系统的监控,老板天天问你:
"今天卖了多少单?" 📊
"库存还剩多少?" 📦
"API响应慢不慢?" ⏱️
"订单金额分布如何?" 💰
这时候,Prometheus的四大指标类型就派上用场了!让我们通过一个真实的电商系统来搞懂它们。
Prometheus 是一个开源的系统监控和告警工具包,它以其多维数据模型、强大的查询语言 PromQL、高效的时间序列数据库以及灵活的告警机制而闻名。
Prometheus 的核心是其时间序列数据。一个时间序列由一个指标名称(metric name)和一组标签(labels)唯一确定。
例如,http_requests_total{method="POST", handler="/api/messages"} 就是一个时间序列,它记录了对 /api/messages 路径的 POST 请求总数。
理解 Prometheus 的第一步,就是理解它如何收集和分类这些数据,这就要从它的四种基本指标类型说起。
指标类型 | 特性 | 电商案例 | 适用场景 | 关键操作 |
|---|---|---|---|---|
Counter | 只增不减的累计值 | 订单总量 | 计数类指标 |
|
Gauge | 可增可减的瞬时值 | 库存数量 | 状态类指标 |
|
Histogram | 观察值的分布统计 | API响应时间 | 性能分析 |
|
Summary | 观察值的分位数统计 | 订单金额分布 | 业务分析 |
|
Counter: 像计步器,只会往上加 📈
Counter 是一个只增不减的累积型指标。它通常用于记录应用启动以来发生的事件总数,例如处理的 HTTP 请求总数、完成的任务数或出现的错误数。
比如,订单数量:今天1000单,明天1050单,错误次数:累计错误500次
Gauge: 像温度计,可上可下 🌡️
Gauge 是一个可以任意增减的瞬时值指标。它通常用于表示那些可以上下波动的测量值,例如当前的内存使用量、CPU 负载、队列中的任务数或活动的线程数。
比如,库存数量:现在100件,卖了10件剩90件,在线用户:当前1000人在线
Histogram: 服务端聚合,客户端分桶 📊
Histogram 主要用于观察和分析事件的分布情况,最常见的场景是监控请求延迟或响应大小。它会将一段时间内的数据采样,并将其计入可配置的存储桶(bucket)中,同时也会提供所有采样值的总和(sum)和总数(count)。
优点是可以聚合多个实例的数据,但缺点主要在于分位数是估算的。主要适合API响应时间、请求大小等场景。
Summary: 客户端聚合,精确分位数 🎯
Summary 与 Histogram 类似,也用于观察事件的分布。但它不通过分桶,而是在客户端直接计算和存储分位数(Quantile)。
其优点在于分位数精确,缺点在于无法跨实例聚合,主要适合于业务指标、SLA监控。

我们的电商系统定义了四种核心指标:
// Counter: 订单总量 - 只增不减的累计指标
var orderTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "ecommerce_orders_total",
Help: "电商系统订单总数",
},
[]string{"status", "payment_method"}, // 标签:订单状态、支付方式
)
// Gauge: 库存数量 - 可增可减的瞬时指标
var inventoryStock = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "ecommerce_inventory_stock",
Help: "商品库存数量",
},
[]string{"product_id", "category"}, // 标签:商品ID、分类
)
// Histogram: API响应时间分布 - 观察值的分布情况
var apiDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "ecommerce_api_duration_seconds",
Help: "API请求响应时间分布",
Buckets: prometheus.DefBuckets, // 默认桶
},
[]string{"method", "endpoint", "status_code"}, // 标签
)
// Summary: 订单金额分布 - 观察值的分位数统计
var orderAmount = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "ecommerce_order_amount_yuan",
Help: "订单金额分布统计",
Objectives: map[float64]float64{
0.5: 0.05, // 50分位数,误差5%
0.9: 0.01, // 90分位数,误差1%
0.99: 0.001, // 99分位数,误差0.1%
},
},
[]string{"user_type", "promotion"}, // 标签
)创建订单 - 四大指标的综合应用
func (e *ECommerceSystem) CreateOrder(productID string, quantity int, amount float64, userType, paymentMethod string) {
start := time.Now()
// 模拟API处理时间
processingTime := time.Duration(rand.Intn(500)+50) * time.Millisecond
time.Sleep(processingTime)
// 检查库存
if stock, exists := e.products[productID]; exists && stock >= quantity {
// 库存充足,创建订单成功
e.products[productID] -= quantity
// 🔢 Counter: 订单成功计数 +1
orderTotal.WithLabelValues("success", paymentMethod).Inc()
// 📊 Gauge: 更新库存数量
inventoryStock.WithLabelValues(productID, "electronics").Set(float64(e.products[productID]))
// 💰 Summary: 记录订单金额
orderAmount.WithLabelValues(userType, "none").Observe(amount)
// ⏱️ Histogram: 记录API响应时间
apiDuration.WithLabelValues("POST", "/api/orders", "200").Observe(time.Since(start).Seconds())
log.Printf("✅ 订单创建成功: 商品=%s, 数量=%d, 金额=%.2f元", productID, quantity, amount)
} else {
// 库存不足,订单失败
orderTotal.WithLabelValues("failed", paymentMethod).Inc()
apiDuration.WithLabelValues("POST", "/api/orders", "400").Observe(time.Since(start).Seconds())
log.Printf("❌ 订单创建失败: 商品=%s库存不足", productID)
}
}Counter使用技巧:
// ✅ 正确:使用标签区分不同状态
orderTotal.WithLabelValues("success", "alipay").Inc()
// ❌ 错误:Counter不能减少
// orderTotal.Dec() // 这会panic!为什么用 Counter? 因为 订单数量是一个不断累积的量。我们不关心某个瞬间的绝对值(因为它只会越来越大),而是关心它在单位时间内的变化率,比如“每秒请求数”(RPS)。Counter + rate() 函数的组合完美地满足了这一需求。
Gauge使用技巧:
// ✅ 设置绝对值
inventoryStock.WithLabelValues("iphone15", "electronics").Set(100)
// ✅ 增加/减少
inventoryStock.WithLabelValues("iphone15", "electronics").Inc()
inventoryStock.WithLabelValues("iphone15", "electronics").Dec()为什么用 Gauge? 因为库存数量不是一个累积值,它会随着系统的负载情况动态变化。我们关心的是它在某个时间点的确切值,以便判断系统是否健康(例如,队列长度是否过长,是否需要扩容处理节点)。
Histogram vs Summary:
// Histogram: 适合可聚合的性能指标
apiDuration.WithLabelValues("POST", "/api/orders", "200").Observe(0.123)
// Summary: 适合业务分位数分析
orderAmount.WithLabelValues("vip", "double11").Observe(15999.0)为什么用 Histogram? 因为它提供了数据的分布视图。通过观察不同延迟区间的请求数量,我们可以更精确地定位性能瓶颈。例如,如果我们发现大量请求落在了 500ms-1s 的桶里,这就明确指出了一个需要优化的方向。
为什么用 Summary? 如果你的核心需求是获取精确的分位数,并且可以接受其不可聚合的限制,Summary 是一个直接的选择。
直方图与汇总对比
特性 | Histogram | Summary |
|---|---|---|
分位数计算 | 服务端 ( | 客户端 (直接暴露) |
聚合能力 | 可聚合(跨实例计算分位数) | 不可聚合 |
性能开销 | 客户端较低,服务端较高 | 客户端较高,服务端较低 |
配置 | 需要预先定义 buckets | 需要定义 quantiles 和其误差 |
在绝大多数情况下,优先选择 Histogram。它的灵活性和可聚合性使其在现代分布式系统中更为实用。只有在你明确知道不需要聚合,并且对客户端性能开销不敏感时,才考虑使用 Summary。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。