服务监控
服务监控丨Prometheus 四大数据类型详解
本文介绍了 Prometheus 的四大数据类型及其 PromQL 查询语言,帮助开发团队构建强大的可观测性系统。
前言
在微服务和云原生架构的世界中,一套强大的监控系统是保障服务稳定性的基石。Prometheus 作为 CNCF 的明星项目,凭借其简单高效的特性,已成为事实上的云原生监控标准。本文将深入剖析 Prometheus 的四大数据类型及其 PromQL 查询语言,帮助开发团队构建强大的可观测性系统。
结论先行:Prometheus 四大数据类型速览
| 特性 | Counter | Gauge | Histogram | Summary |
|---|---|---|---|---|
| 定义 | 只增不减的累积计数器 | 可增可减的瞬时值 | 观测值分布的分桶统计 | 客户端计算的分位数统计 |
| 重置行为 | 服务重启时归零 | 保持当前值 | 桶计数归零 | 计数归零 |
| 典型应用 | 请求计数、错误数、流量统计 | 温度、内存使用、连接数 | 请求延迟、响应大小 | 请求延迟、队列等待时间 |
| 数据点 | 单一值 | 单一值 | _bucket、_sum、count | {quantile="x"}、_sum、_count |
| 查询重点 | rate()、increase() | 直接使用、预测函数 | histogram_quantile() | 直接读取分位数 |
| 分布式聚合 | 可以(sum、rate) | 可以(avg、max、min) | 可以(百分位也可聚合) | 有限(分位数不可聚合) |
| 资源消耗 | 低 | 低 | 中(依赖桶数量) | 中(客户端计算) |
一、Prometheus 核心数据类型详解
1. Counter(计数器):持续增长的累积值
Counter 是最简单但也最常用的指标类型,代表一个只增不减的累积数值。每当事件发生,计数器增加;当监控目标重启时,计数器归零。
适用场景:
- API 请求总数
- 错误发生次数
- 处理任务的数量
- 网络流量字节数
正确的代码实现:
// 声明带标签的计数器
requestCounter := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "path", "status"}, // 定义标签维度
)
prometheus.MustRegister(requestCounter)
// 使用标签记录请求
requestCounter.WithLabelValues("GET", "/api/users", "200").Inc()
PromQL 查询技巧:
# 每秒请求率(5分钟窗口)
rate(http_requests_total{status="200"}[5m])
# 错误率计算
sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
# 1小时内的请求增量
increase(http_requests_total[1h])
最佳实践:
- 永远不要直接使用 Counter 的原始值,总是使用
rate()或increase() - 使用有意义的标签进行多维度分析,但避免高基数标签
- Counter 重置(如服务重启)会被
rate()函数自动处理
2. Gauge(仪表盘):可变的瞬时值
Gauge 表示一个可增可减的瞬时测量值,反映系统的当前状态。
适用场景:
- 内存使用量
- CPU 使用率
- 当前活跃连接数
- 队列深度
- 温度等物理量
正确的代码实现:
// 声明带标签的仪表盘
memoryGauge := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "app_memory_usage_bytes",
Help: "Current memory usage in bytes",
},
[]string{"component", "instance"},
)
prometheus.MustRegister(memoryGauge)
// 设置当前值
memoryGauge.WithLabelValues("api-server", "instance-1").Set(float64(getCurrentMemoryUsage()))
PromQL 查询技巧:
# 直接使用当前值
app_memory_usage_bytes{component="api-server"}
# 统计聚合
avg_over_time(app_memory_usage_bytes[1h])
max_over_time(app_memory_usage_bytes[24h])
# 趋势预测(线性回归)
predict_linear(app_memory_usage_bytes[6h], 4 * 3600)
# 计算变化率
(app_memory_usage_bytes - app_memory_usage_bytes offset 1h) / app_memory_usage_bytes offset 1h
最佳实践:
- Gauge 可以直接使用其瞬时值,不需要像 Counter 那样使用 rate
- 对于容易波动的指标,考虑使用
avg_over_time平滑数据 - 利用
predict_linear进行容量规划和趋势预测
3. Histogram(直方图):观测值分布的分桶统计
Histogram 允许对观测值(如请求延迟)进行分布式统计,将数据分散到预定义的桶中,是分析性能分布的理想工具。
自动生成的指标:
<metric>_bucket{le="<upper bound>"}: 小于等于特定阈值的观测值计数<metric>_sum: 所有观测值的总和<metric>_count: 观测值总数
适用场景:
- 请求延迟分布
- 响应大小分布
- 批处理任务执行时间
- 任何需要百分位数分析的场景
正确的代码实现:
// 声明带标签的直方图
durationHistogram := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration in seconds",
Buckets: prometheus.ExponentialBuckets(0.001, 2, 10), // 从1ms开始指数增长
},
[]string{"method", "path"},
)
prometheus.MustRegister(durationHistogram)
// 记录请求延迟
durationHistogram.WithLabelValues("GET", "/api/users").Observe(responseTime)
PromQL 查询技巧:
# 计算平均响应时间
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])
# 计算P90延迟
histogram_quantile(0.9, rate(http_request_duration_seconds_bucket[5m]))
# 按API路径分析P95延迟
histogram_quantile(0.95, sum by(path, le) (rate(http_request_duration_seconds_bucket[5m])))
# 计算SLO:延迟小于100ms的请求比例
sum(rate(http_request_duration_seconds_bucket{le="0.1"}[5m])) / sum(rate(http_request_duration_seconds_count[5m]))
最佳实践:
- 仔细设计桶边界,覆盖关键分位数区域
- 对于延迟指标,通常使用指数桶比线性桶更合理
- 利用
histogram_quantile计算任意分位数 - 桶的数量会影响存储和性能,权衡精度和开销
4. Summary(摘要):客户端计算的分位数统计
Summary 与 Histogram 类似,但在客户端直接计算并存储分位数,无需服务器端计算。
自动生成的指标:
<metric>{quantile="<φ>"}: φ 分位数的值<metric>_sum: 所有观测值的总和<metric>_count: 观测值总数
适用场景:
- 需要高精度分位数的场景
- 客户端计算分位数更高效的情况
- 对服务器端聚合要求不高的场景
正确的代码实现:
// 声明带标签的摘要
durationSummary := prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "http_request_duration_seconds_summary",
Help: "HTTP request duration in seconds",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
},
[]string{"method", "path"},
)
prometheus.MustRegister(durationSummary)
// 记录请求延迟
durationSummary.WithLabelValues("POST", "/api/login").Observe(responseTime)
PromQL 查询技巧:
# 直接读取P99延迟
http_request_duration_seconds_summary{quantile="0.99", method="GET", path="/api/users"}
# 计算平均响应时间
rate(http_request_duration_seconds_summary_sum[5m]) / rate(http_request_duration_seconds_summary_count[5m])
# 每个服务的中位数延迟
max by(service) (http_request_duration_seconds_summary{quantile="0.5"})
最佳实践与限制:
- Summary 预计算的分位数不能跨实例聚合(这是关键限制)
- 适用于分位数精度要求高且实例相对独立的场景
- 客户端计算分位数会增加应用资源消耗
- 分位数设置后不可更改,需提前规划好监控需求
二、PromQL 查询语言精通
PromQL 是 Prometheus 的强大武器,掌握它能让我们精确提取所需的监控数据。
1. 基础查询与标签选择
# 基本查询与精确匹配
http_requests_total{status="200", method="GET"}
# 正则表达式匹配
http_requests_total{path=~"/api/v1/.+", method!="OPTIONS"}
# 范围查询(返回时间序列)
http_requests_total{status="500"}[5m]
2. 操作符与函数
算术运算符:
# 计算内存使用率百分比
100 * (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes
聚合函数:
# 按服务和路径分组求和
sum by(service, path) (rate(http_requests_total[5m]))
# 丢弃instance标签求最大值
max without(instance) (node_cpu_seconds_total)
瞬时向量函数:
# 标签替换
label_replace(up, "host", "$1", "instance", "(.*):.*")
# 按标签分组取topk
topk by(path) (5, http_request_duration_seconds_sum / http_request_duration_seconds_count)
3. 复杂查询模式
SLI/SLO 监控:
# 服务可用性SLI
sum(rate(http_requests_total{status=~"2..|3.."}[5m])) / sum(rate(http_requests_total[5m]))
# 延迟SLO
histogram_quantile(0.99, sum by(le) (rate(http_request_duration_seconds_bucket[5m]))) < 0.3
异常检测:
# 相对于历史同期的异常增长
rate(http_requests_total[5m])
> 2 * avg_over_time(rate(http_requests_total[5m])[1d:5m] offset 1d)
预测分析:
# 磁盘空间预测
predict_linear(node_filesystem_free_bytes{mountpoint="/"}[6h], 7 * 24 * 3600) < 10 * 1024 * 1024 * 1024
三、实战应用场景
1. 服务健康度监控
RED 方法实现:
# Rate - 请求率
sum by(service) (rate(http_requests_total[5m]))
# Error - 错误率
sum by(service) (rate(http_requests_total{status=~"5.."}[5m])) / sum by(service) (rate(http_requests_total[5m]))
# Duration - P95延迟
histogram_quantile(0.95, sum by(service, le) (rate(http_request_duration_seconds_bucket[5m])))
服务依赖健康度:
# 数据库查询错误率
sum(rate(database_query_errors_total[5m])) / sum(rate(database_queries_total[5m]))
# 第三方API调用延迟
histogram_quantile(0.99, sum by(api_name, le) (rate(api_request_duration_seconds_bucket[5m])))
2. 性能瓶颈分析
热点 API 发现:
# 延迟最高的10个接口
topk(10,
histogram_quantile(0.95, sum by(method, path, le) (rate(http_request_duration_seconds_bucket[5m])))
)
# 请求量最大的接口
topk(10, sum by(method, path) (rate(http_requests_total[5m])))
数据库性能分析:
# 平均查询时间趋势
rate(db_query_duration_seconds_sum[5m]) / rate(db_query_duration_seconds_count[5m])
# 慢查询比例
sum(rate(db_query_duration_seconds_bucket{le="+Inf"}[5m])) - sum(rate(db_query_duration_seconds_bucket{le="0.1"}[5m])) / sum(rate(db_query_duration_seconds_bucket{le="+Inf"}[5m]))
3. 容量规划与告警
资源预测:
# CPU使用率预测
predict_linear(avg by(instance) (rate(node_cpu_seconds_total{mode!="idle"}[6h])) [3d:], 7 * 24 * 3600) > 0.85
# 内存压力告警
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.9
流量容量规划:
# 带宽使用预测
predict_linear(rate(node_network_transmit_bytes_total[12h])[7d:], 30 * 24 * 3600)
四、最佳实践与性能优化
1. 指标命名与标签设计
命名规范:
- 使用 snake_case
- 包含单位后缀(_bytes, _seconds, _total)
- 保持风格一致性
标签最佳实践:
// 合理设计标签维度
apiLatency := prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "api_request_duration_seconds",
Help: "API request duration in seconds",
Buckets: prometheus.ExponentialBuckets(0.001, 2, 10),
},
[]string{"service", "endpoint", "status_code"}, // 合理的低基数标签
)
// 不可变标签使用ConstLabels
prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "service_info",
Help: "Service information",
ConstLabels: prometheus.Labels{"version": "v2.1.3", "environment": "production"},
},
[]string{"instance"},
)
2. 客户端性能优化
// 缓存常用标签组合以提高性能
getCounter := requestCounter.WithLabelValues("GET", "/api/users", "200")
for i := 0; i < 100; i++ {
getCounter.Inc() // 重用标签组合,避免重复创建
}
// 批量更新方式
var rpcDurations = prometheus.NewSummaryVec(
prometheus.SummaryOpts{
Name: "rpc_durations_seconds",
Help: "RPC latency distributions.",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
},
[]string{"service"},
)
func ObserveBatch(durations map[string]float64) {
for service, duration := range durations {
rpcDurations.WithLabelValues(service).Observe(duration)
}
}
3. 查询优化
# 优化前:高基数查询
sum(rate(http_requests_total{path=~"/api/.*"}[5m])) by (path, method, status)
# 优化后:降低基数,按需聚合
sum(rate(http_requests_total{path=~"/api/.*"}[5m])) by (method, status)
# 优化聚合顺序(先聚合再求和)
sum(
avg by(instance) (rate(node_cpu_seconds_total{mode!="idle"}[5m]))
)
五、常见陷阱与解决方案
1. 高基数问题
问题:标签组合过多导致时间序列爆炸
解决方案:
- 限制标签基数,避免使用 UserID、SessionID 等作为标签
- 使用
label_replace和正则表达式转换高基数标签 - 考虑使用 Exemplars 而非标签存储高基数数据
2. 数据类型选择误区
Counter vs Gauge:请求数应使用 Counter 而非 Gauge
Histogram vs Summary:需要聚合分析请使用 Histogram,精确分位数可选 Summary
3. 查询性能问题
问题:复杂查询导致 Prometheus 高负载
解决方案:
- 使用记录规则预计算常用查询
- 合理设置 scrape 间隔,避免过度采集
- 对高请求量接口使用客户端聚合
总结与展望
Prometheus 的四种数据类型各有所长:Counter 适合累积事件计数,Gauge 适合瞬时状态测量,Histogram 适合分布统计和百分位分析,Summary 适合客户端精确分位数计算。与之配合的 PromQL 提供了强大的数据查询和分析能力,共同构成了完整的监控解决方案。
随着云原生技术的发展,Prometheus 生态也在不断壮大,与 Grafana、Alertmanager、Thanos 等工具集成,能够构建更完善的监控告警平台。在微服务架构中,结合 RED(Rate、Error、Duration)和 USE(Utilization、Saturation、Errors)方法论,可以构建全面的可观测性系统。
无论你是刚开始使用 Prometheus 的新手,还是寻求优化监控系统的资深工程师,希望本文对你理解和应用 Prometheus 有所帮助。记住,好的监控不仅能及时发现问题,更能预测和防范问题,最终服务于业务可靠性和用户体验的提升。
参考资源:
- Prometheus 官方文档: https://prometheus.io/docs/
- Google SRE 书籍: https://sre.google/sre-book/monitoring-distributed-systems/
- Prometheus 实战: https://prometheusbook.com/
来自开放网络的回应
引用、喜欢、转发和站外回复会通过 Webmention 回到这里。
接收与展示代码已经就绪,注册正式域名后即可开始收集回应。
评论
评论由 GitHub Discussions 保存和管理。