2. Prometheus 核心概念:Pull模型、时间序列数据库、Metric类型、PromQL入门

好,咱们进入正题。这一章我打算把Prometheus最核心的几个概念掰开揉碎了讲清楚。你可能会觉得概念很枯燥,但相信我,搞懂了这些,后面搭建系统就跟玩似的。

2.1 Pull模型:为什么Prometheus要主动拉数据?

Prometheus最鲜明的特点,就是它的数据采集方式——Pull模型。说白了,就是Prometheus Server主动去各个目标机器上“拉”数据,而不是等着目标机器把数据“推”过来。

我刚开始接触时也觉得奇怪:为什么不用更常见的Push方式?后来在生产环境踩过坑才明白,Pull模型有几个天然优势:

  • 可控性更强:Prometheus自己决定拉取频率和时机,不会被客户端的突发流量冲垮。
  • 健康检测更简单:如果某个目标拉不到数据,基本可以判定它挂了。
  • 配置即发现:你只需要告诉Prometheus“去这些地址拉数据”,它自己会搞定。
我的经验: 我曾经在一个Kubernetes集群里,因为某个Pod疯狂推数据,直接把监控系统的网络打爆了。换成Pull模型后,这种问题再也没出现过。说白了,主动权在自己手里,心里踏实。

当然,Pull模型也有短板。比如在动态环境里,服务实例频繁启停,Prometheus怎么知道该去拉谁?这就引出了服务发现机制,咱们后面会专门讲。

2.2 时间序列数据库:数据是怎么存的?

Prometheus的存储引擎,本质上是一个时间序列数据库。什么叫时间序列?就是每个指标都带着时间戳和标签,按时间顺序排列。

举个例子,你监控一台服务器的CPU使用率:

node_cpu_seconds_total{cpu="0", mode="user"} 12345.67
node_cpu_seconds_total{cpu="0", mode="system"} 2345.67

这里node_cpu_seconds_total是指标名,{cpu="0", mode="user"}是标签,12345.67是值。每次采集都会生成一个新样本,带上当前时间戳。

我个人的习惯是,把时间序列数据库想象成一个巨大的Excel表格:

  • 每一行是一个时间点的数据
  • 每一列是一个唯一的指标组合(指标名+标签)
  • 行数随时间无限增长

你想想看,如果所有数据都存着,磁盘迟早会爆。所以Prometheus有自己的压缩和过期策略。默认情况下,它会保留15天的数据,然后自动清理旧数据。

注意: 我曾经遇到过一个案例,因为没调整保留策略,导致监控数据把磁盘写满了,连告警都发不出来。所以生产环境一定要根据你的磁盘容量,合理设置 --storage.tsdb.retention.time 参数。

2.3 Metric类型:四种核心类型

Prometheus的指标类型,说白了就是四种:Counter、Gauge、Histogram、Summary。每种类型都有它的脾气,用错了会出笑话。

2.3.1 Counter:只增不减的计数器

Counter代表一个只增不减的数值。比如请求总数、错误总数、CPU时间片数。它最典型的用法是:

  • 统计HTTP请求总量
  • 统计数据库查询次数
  • 统计任务执行次数

你可能会问:如果服务重启了,Counter会归零吗?会的。所以用Counter算增长率时,要小心重启带来的跳变。我一般会用rate()函数来平滑处理。

# 计算每秒请求增长率
rate(http_requests_total[5m])

2.3.2 Gauge:可增可减的仪表盘

Gauge代表一个可以上下波动的数值。比如内存使用量、CPU温度、在线用户数。它没有“只增不减”的限制,想怎么变就怎么变。

我记得有一次排查内存泄漏问题,就是用Gauge类型的process_resident_memory_bytes指标,配合delta()函数,一眼就看出了内存持续增长的趋势。

# 查看内存变化量
delta(process_resident_memory_bytes[1h])

2.3.3 Histogram:分布统计的利器

Histogram用于统计数据的分布情况。比如:

  • 90%的请求在多少毫秒内完成?
  • 大部分请求的响应时间集中在哪个区间?

它会把数据分到不同的桶(bucket)里。比如你定义桶为{0.1, 0.5, 1, 5}秒,那么Histogram会统计出:

  • 响应时间 ≤ 0.1秒的请求有多少
  • 响应时间 ≤ 0.5秒的请求有多少
  • 以此类推

用Histogram算P99延迟,是监控里的经典操作:

histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
避坑指南: 我曾经因为桶的范围设置不合理,导致大部分请求都落在了最后一个桶里,P99算出来完全不准。后来我根据业务经验,把桶的范围调整到合理区间,数据才变得有意义。

2.3.4 Summary:自带分位数的统计

Summary和Histogram类似,但它直接在客户端算好了分位数。比如你配置了0.5、0.9、0.99分位数,Summary会直接暴露这些值。

它的好处是查询简单,不用写复杂的PromQL。但缺点也很明显:分位数是固定的,不能事后调整。而Histogram可以灵活查询任意分位数。

我个人更倾向于用Histogram,因为灵活性更高。除非你明确知道需要哪些分位数,并且不想在查询时增加计算负担。

2.4 PromQL入门:查询语言其实没那么难

PromQL是Prometheus的查询语言,说白了就是用来从时间序列数据库里捞数据的。刚开始看可能觉得像天书,但掌握几个核心函数后,你会发现它其实很直观。

2.4.1 最简单的查询:直接查指标

node_memory_MemTotal_bytes

这条语句会返回所有节点的总内存。如果有多台机器,你会看到多条时间序列。

2.4.2 加标签过滤:精确锁定目标

node_memory_MemTotal_bytes{instance="192.168.1.1:9100"}

用花括号加标签过滤,只查特定实例的数据。标签支持精确匹配(=)、正则匹配(=~)、反向匹配(!=)。

2.4.3 范围查询:看一段时间内的数据

node_memory_MemTotal_bytes[5m]

加上[5m],表示查询过去5分钟内的所有样本点。返回的是一个数组,而不是单个值。

2.4.4 常用函数:让数据说话

函数 作用 示例
rate() 计算Counter的每秒增长率 rate(http_requests_total[5m])
irate() 计算瞬时增长率(更敏感) irate(http_requests_total[5m])
avg() 计算平均值 avg(node_cpu_seconds_total)
sum() 求和 sum(node_memory_MemTotal_bytes)
topk() 取前N个最大值 topk(3, node_memory_MemFree_bytes)

举个例子,你想看所有机器的CPU使用率:

100 - (avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100)

这条语句的意思是:先算出每台机器的空闲CPU占比,然后用100减去它,得到使用率。嗯,看着复杂,但拆开来看,每一步都很清晰。

核心要点: PromQL的威力在于组合。你可以把多个函数和标签过滤嵌套在一起,实现非常复杂的查询逻辑。我建议你从简单的查询开始,逐步增加复杂度,慢慢就会上手。

2.5 本章小结

这一章我们聊了Prometheus的四个核心概念:

  • Pull模型:主动拉取,可控性强
  • 时间序列数据库:指标+标签+时间戳,按时间存储
  • 四种Metric类型:Counter、Gauge、Histogram、Summary,各有各的用途
  • PromQL入门:从简单查询到组合函数,一步步掌握

下一章,我会带你亲手搭建一个Prometheus实例,把今天学的概念都用起来。到时候你会发现,理论结合实践,才是最快的成长方式。