3. Prometheus核心概念:数据模型与指标类型

聊到Prometheus,很多人第一反应是「哦,一个监控系统」。但说实话,真正用好它,你得先搞懂它的数据模型。这就像学开车,你得先知道油门刹车在哪,对吧?

我刚开始接触Prometheus时,也踩过不少坑。有一次生产环境告警没触发,查了半天,发现是我对指标类型的理解出了问题。嗯,从那以后,我每次讲Prometheus,都会把数据模型和指标类型放在最前面讲。

3.1 数据模型:Metric、Label、Sample

Prometheus的数据模型,说白了就三个核心概念:指标(Metric)标签(Label)样本(Sample)。它们组合在一起,构成了Prometheus存储和查询的基础。

3.1.1 Metric(指标)

指标就是你要监控的「东西」。比如CPU使用率、内存占用量、HTTP请求数。每个指标都有一个名字,命名规则很简单:namespace_subsystem_unit_suffix。举个例子:

# 指标名示例
node_cpu_seconds_total
http_requests_total
prometheus_tsdb_head_series

我个人习惯用下划线分隔,清晰明了。你想想看,如果指标名写成nodeCPUSecondsTotal,是不是看着就头疼?

3.1.2 Label(标签)

标签是Prometheus最强大的设计之一。它让你可以对同一个指标进行多维度的细分。每个标签是一个键值对,比如:

http_requests_total{method="GET", endpoint="/api/v1/users", status="200"}

这里methodendpointstatus就是标签。你可以通过标签组合,查询出「GET请求中,/api/v1/users接口,返回200的次数」。这在排查问题时特别有用。

注意:标签值不能太多。我在项目中遇到过有人给每个请求都加了一个唯一的request_id标签,结果Prometheus直接OOM了。标签基数(cardinality)过高,是Prometheus最常见的性能杀手。

3.1.3 Sample(样本)

样本就是实际的数据点。它包含三个部分:

  • 时间戳:精确到毫秒
  • :float64浮点数
  • 指标+标签:标识这个样本属于哪个时间序列

一个样本长这样:

# 时间戳: 值
node_cpu_seconds_total{cpu="0", mode="idle"} 1234567890.123 382345.67

说白了,Prometheus就是不断地收集这些样本,然后存起来。你查询的时候,它把样本按时间序列组织好,返回给你。

3.2 四种指标类型

Prometheus有四种核心指标类型。我刚开始学的时候,觉得Counter和Gauge差不多,后来才发现区别大了去了。

3.2.1 Counter(计数器)

Counter只增不减。它用来记录累计值,比如:

  • HTTP请求总数
  • CPU时间累计
  • 错误发生次数

代码示例:

// Go语言示例
httpRequestsTotal := prometheus.NewCounter(prometheus.CounterOpts{
    Name: "http_requests_total",
    Help: "Total number of HTTP requests.",
})

// 每次请求加1
httpRequestsTotal.Inc()

你可能会问:「那我想看每秒的请求量怎么办?」用rate()函数啊:

rate(http_requests_total[5m])

这个函数会计算过去5分钟内,每秒平均增长量。我在项目中经常用这个来监控流量突增。

3.2.2 Gauge(仪表盘)

Gauge可增可减。它用来记录瞬时值,比如:

  • 当前内存使用量
  • 当前在线用户数
  • 队列长度

代码示例:

memoryUsage := prometheus.NewGauge(prometheus.GaugeOpts{
    Name: "memory_usage_bytes",
    Help: "Current memory usage in bytes.",
})

// 设置值
memoryUsage.Set(1024 * 1024 * 512) // 512MB

// 增加/减少
memoryUsage.Add(1024 * 1024) // 增加1MB
memoryUsage.Sub(1024 * 1024) // 减少1MB
小技巧:Gauge配合avg_over_time()函数,可以计算一段时间内的平均值。比如监控CPU使用率,用avg_over_time(node_cpu_usage[5m]),能过滤掉瞬时抖动。

3.2.3 Histogram(直方图)

Histogram用来统计数据的分布情况。它会把数据分到不同的桶(bucket)里。比如HTTP请求延迟:

# 定义桶:0.1s, 0.5s, 1s, 2s, 5s
http_request_duration_seconds_bucket{le="0.1"} 1024
http_request_duration_seconds_bucket{le="0.5"} 2048
http_request_duration_seconds_bucket{le="1"} 3072
http_request_duration_seconds_bucket{le="2"} 3584
http_request_duration_seconds_bucket{le="5"} 4096
http_request_duration_seconds_bucket{le="+Inf"} 4096

你看,le="0.1"表示延迟小于等于0.1秒的请求有1024个。通过这个,你可以算出P50、P99等分位数。

代码示例:

requestDuration := prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    "http_request_duration_seconds",
    Help:    "HTTP request duration in seconds.",
    Buckets: prometheus.DefBuckets, // 默认桶:[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
})

// 记录一个请求耗时0.3秒
requestDuration.Observe(0.3)
避坑指南:我曾经在生产环境把桶设得太细,比如[0.001, 0.002, 0.003, ...],结果Prometheus内存暴涨。桶的数量建议控制在10-20个,太多会影响性能。

3.2.4 Summary(摘要)

Summary和Histogram有点像,但它直接计算分位数,不需要你事后算。不过它有个缺点:分位数是客户端算的,你没法在查询时动态调整。

requestDuration := prometheus.NewSummary(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%
    },
})

requestDuration.Observe(0.3)

我个人习惯:需要精确分位数用Histogram,需要低延迟查询用Summary。Histogram更灵活,但查询时计算分位数会消耗更多资源。

3.3 Pull模型原理

Prometheus采用Pull模型,而不是Push。什么意思呢?就是Prometheus主动去拉取数据,而不是等应用把数据推过来。

流程是这样的:

  1. 应用暴露一个HTTP端点,比如/metrics
  2. Prometheus定期(默认15秒)去这个端点拉数据
  3. 拉回来的数据经过解析、存储,就可以查询了

为什么用Pull而不是Push?我总结了几点:

  • 更容易控制采集频率:Prometheus自己决定什么时候拉,不会因为应用推得太快而 overload
  • 更容易发现故障:如果应用挂了,Prometheus拉不到数据,立刻就能发现
  • 更容易做安全控制:只需要在Prometheus端配置认证,不需要每个应用都配

当然,Pull模型也有缺点。比如应用在防火墙后面,Prometheus可能拉不到。这时候可以用Pushgateway做中转。

核心逻辑图:下面这张SVG图展示了Prometheus的数据流转过程。

Prometheus Pull模型数据流转 应用实例1 /metrics 端点 应用实例2 /metrics 端点 应用实例3 /metrics 端点 Prometheus Server 定时拉取 (默认15s) 解析 → 存储 → 查询 HTTP GET /metrics HTTP GET /metrics HTTP GET /metrics TSDB 存储 时间序列数据库 PromQL 查询 Grafana / API

这张图很直观:Prometheus Server主动去拉各个应用的/metrics端点,数据存到TSDB,然后通过PromQL查询。整个过程就是「拉-存-查」三步走。

经验之谈:我在搭建生产环境时,会把Pull间隔从15秒改成30秒。为什么呢?因为15秒对大多数场景来说太频繁了,反而会增加网络和存储压力。除非你的业务对实时性要求极高,否则30秒完全够用。

好了,Prometheus的数据模型和指标类型就讲到这里。记住一句话:Metric是名字,Label是维度,Sample是数据。搞懂这三者的关系,Prometheus就算入门了。


公众号:蓝海资料掘金营,微信deep3321