Prometheus已经成为可观测领域指标领域最常用的技术。
一、了解时序数据
指标是以时序数据(Time Series)的方式表达的,什么是时序数据呢?
我们以Prometheus采集机器cpu指标示例:
ini
node_cpu_seconds_total{cpu="0", mode="user", instance="192.168.1.10:9100"} 1345.67 1701409200000
node_cpu_seconds_total{cpu="0", mode="system", instance="192.168.1.10:9100"} 2345.89 1701409200000
上面展示了Prometheus时序数据的数据结构:
-
指标名称(Metric Name) :描述数据的类型,例如
node_cpu_seconds_total
。 -
标签(Labels) :以键值对的形式为数据提供上下文,例如
{cpu="0", mode="user", instance="192.168.1.10:9100"}
。 -
时间戳(Timestamp) :每个数据点都有一个关联的时间戳,例如
1701409200000
。 -
样本值(Sample Value) :实际的数值数据,通常为浮点数,例如
1345.67
。
而时序库(Time series database)就是一种专门处理时序数据的数据库,其中InfluxDB、Prometheus、VictoriaMetrics、M3DB 等都是时序库。
二、时序数据类型
Prometheus 支持4数据类型,分为 Gauge、Counter、Histogram、Summary。
1. Counter(计数器)
定义
Counter
是一种只能递增的指标数据类型,用于表示累积值。计数器只能在特定情况下(如系统重启或数据重置时)归零,不能减少。
用途
适用于累计量统计场景,例如:
- HTTP 请求总数
- 错误总数
- 已处理的任务总数
数据结构
计数器的值随着时间递增,例如:
ini
http_requests_total{method="GET", status="200"} 1024 1701409200000
http_requests_total{method="POST", status="500"} 10 1701409200000
上述数据表示:
- 接收到 1024 次 GET 请求,且 HTTP 状态码为 200;
- 接收到 10 次 POST 请求,且 HTTP 状态码为 500。
使用示例
在 PromQL 查询中,可以通过 rate()
或 increase()
函数计算计数器的增长速率或增加量。例如:
- 查询最近 5 分钟的请求增长速率:
scss
rate(http_requests_total[5m])
- 查询最近 5 分钟的请求总增长量:
scss
increase(http_requests_total[5m])
2. Gauge(仪表盘)
定义
Gauge
是一种可以任意增加或减少的指标数据类型,通常用于表示瞬时值,例如系统状态或资源使用情况。
用途
适用于描述当前状态的场景,例如:
- 当前 CPU 使用率
- 内存使用量
- 磁盘剩余空间
数据结构
Gauge
的值随时间波动,例如:
ini
node_memory_available_bytes{instance="192.168.1.10:9100"} 1048576000 1701409200000
node_cpu_usage_percentage{instance="192.168.1.10:9100"} 75.5 1701409200000
上述数据表示:
- 当前可用内存为 1GB;
- 当前 CPU 使用率为 75.5%。
使用示例
查询当前的瞬时值:
node_memory_available_bytes
计算过去 5 分钟的平均值:
scss
avg_over_time(node_cpu_usage_percentage[5m])
3. Histogram(直方图)
定义
Histogram
将数据分布到多个桶中,用于统计数据的分布范围,例如响应时间或延迟。Histogram
指标通常伴随以下三个子指标:
_bucket
:表示落在每个桶范围内的样本数;_count
:样本总数;_sum
:样本值的总和。
用途
适用于数据分布分析场景,例如:
- 请求延迟的分布;
- 数据传输大小的分布。
数据结构
假设我们需要统计请求延迟:
ini
plaintext
http_request_duration_seconds_bucket{le="0.1"} 2400 1701409200000
http_request_duration_seconds_bucket{le="0.5"} 5000 1701409200000
http_request_duration_seconds_bucket{le="1.0"} 7500 1701409200000
http_request_duration_seconds_bucket{le="+Inf"} 10000 1701409200000
http_request_duration_seconds_count 10000 1701409200000
http_request_duration_seconds_sum 7200 1701409200000
上述数据表示:
- 延迟 ≤ 0.1 秒的请求有 2400 次;
- 延迟 ≤ 0.5 秒的请求有 5000 次;
- 延迟 ≤ 1.0 秒的请求有 7500 次;
- 总请求数为 10000,延迟总和为 7200 秒。
使用示例
- 查询过去 5 分钟内每个桶的请求速率:
scss
rate(http_request_duration_seconds_bucket[5m])
- 查询平均延迟:
scss
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])
4. Summary(摘要)
定义
Summary
是对数据的统计摘要,直接提供分位数(Quantile)值,例如中位数(50% 分位数)、90% 分位数、99% 分位数等。
用途
适用于单实例的统计场景,例如:
- 请求延迟的统计摘要;
- 数据处理时间的统计摘要。
数据结构
假设我们需要统计请求延迟的分位数:
ini
http_request_duration_seconds{quantile="0.5"} 0.3
http_request_duration_seconds{quantile="0.9"} 0.8
http_request_duration_seconds{quantile="0.99"} 1.2
上述数据表示:
- 50% 的请求延迟 ≤ 0.3 秒;
- 90% 的请求延迟 ≤ 0.8 秒;
- 99% 的请求延迟 ≤ 1.2 秒。
使用示例
- 查询中位数延迟:
ini
http_request_duration_seconds{quantile="0.5"}
Histogram 与 Summary 的对比
5.1 简单比较
特点 | Histogram | Summary |
---|---|---|
数据存储方式 | 分桶(_bucket) | 提供分位数(quantile) |
全局聚合能力 | 支持多实例聚合 | 不支持全局聚合 |
分位数计算 | 查询时由server端动态计算 | client端预先计算好 |
适用场景 | 适合多实例场景,如全球延迟分布统计 | 适合单实例场景,如本地延迟摘要 |
5.2 如果要计算百分数,选Histogram 还是 Summary?
在实际使用时,笔者曾经使用Summary来计算百分数,例如计算方法、接口的请求耗时P50、P90的值,但是最终发现,还是Histogram更加适合,下面是Histogram 还是 Summary选型对比。
维度 | Histogram | Summary |
---|---|---|
百分位计算方式 | 百分位数由桶区间和数据分布推导而来,精度依赖桶设计。 | 百分位数由客户端直接计算,精度高且不受分布影响。 |
动态分布适配性 | 若分布变化大(如从 100ms 到 4000ms),需更多桶支持,增加存储和计算开销。 | 不依赖桶设计,无需调整参数,能直接适应动态分布,始终保持高精度。 |
多实例聚合能力 | 支持多实例和跨指标聚合(如 sum、avg),适合多节点大规模监控。 | 不支持跨实例聚合,主要适合单实例监控。 |
计算资源消耗 | 服务端依赖桶设计计算百分位,若桶多,计算和存储开销较大。 | 客户端直接计算百分位,服务端资源占用小,但客户端负载较高。 |
适用场景 | 适合需要多实例聚合、多指标关联分析场景,或对绝对精度要求较低的情况。 | 适合单实例、需要高精度百分位数分析的场景,尤其是分布动态变化较大的情况。 |
总结:
Summary适用于单实例,精度相比Histogram精度更准确,但是当前线上环境基本都是分布式多实例架构,而Summary不适用于多实例聚合计算,这是致命弱点。
所以,对于绝大部分场景而言,Histogram比Summary更适合计算百分位,只是需要注意,Histogram需要合理地设置桶的区间大小和数量,使得在可接受的存储、计算性能消耗下,计算出精度相对准确的百分位数。