3. Prometheus 核心概念:数据模型、Pull模型、PromQL入门

好,咱们今天聊聊 Prometheus 最核心的几个概念。说实话,我刚开始接触 Prometheus 的时候,也觉得它跟传统的监控系统不太一样。但用顺手之后,你会发现它的设计其实非常优雅。

我个人习惯把 Prometheus 比作一个「时间序列数据库」+「抓取器」的组合体。它不主动推数据,而是定期去拉。这个思路,说白了就是「你来不来,我都在这里」。嗯,咱们一个一个来看。

3.1 数据模型:标签是灵魂

Prometheus 的数据模型,核心就一句话:每个时间序列由指标名和一组键值对标签唯一标识

举个例子:

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

这里 http_requests_total 是指标名,花括号里的是标签。标签就是维度,你可以用它做任意维度的聚合、过滤、分组。

我在项目中遇到过一个问题:有个同事把所有请求都打到一个指标里,没有区分 method 和 endpoint。结果排查故障时,根本看不出是哪个接口出了问题。你想想看,一个指标里混着 GET、POST、DELETE,还有各种路径,这怎么查?

所以我的建议是:标签设计要提前规划好。常见的标签包括:

  • 业务维度:service、endpoint、method
  • 环境维度:env(prod/staging/dev)、region
  • 技术维度:instance、job、version

但也要注意,标签不是越多越好。每个标签组合都会产生一个新的时间序列。如果标签基数太大(比如把 user_id 放进去),Prometheus 的内存和存储会爆炸。我曾经见过一个团队把 request_id 当标签,结果 Prometheus 直接 OOM 了……嗯,这个坑大家别踩。

⚠️ 标签基数警告:避免使用高基数标签(如 user_id、session_id、IP 地址)。建议控制在 10 万以内的时间序列总数。

3.2 Pull 模型:主动出击,而非被动等待

传统监控系统(比如 Zabbix)是 Agent 主动推数据到 Server。Prometheus 反其道而行之——它主动去拉(Pull)目标暴露的指标端点。

为什么会这样?我个人觉得有几点好处:

  1. 更容易控制采集频率:Prometheus 自己决定什么时候拉,拉多快。不会被突发的数据洪流冲垮。
  2. 健康检查天然集成:如果目标挂了,Prometheus 拉不到数据,立马就能发现。不需要额外的心跳机制。
  3. 更容易做水平扩展:多个 Prometheus 实例可以各自拉各自的目标,互不干扰。

配置方式也很简单,在 prometheus.yml 里定义 scrape_configs

scrape_configs:
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['localhost:9100']
        labels:
          env: 'prod'
  - job_name: 'api-service'
    metrics_path: '/metrics'
    scrape_interval: 15s
    static_configs:
      - targets: ['192.168.1.10:8080', '192.168.1.11:8080']

这里有个细节:scrape_interval 默认是 1 分钟,但你可以按需调整。我一般把核心服务设为 15 秒,非核心的 30 秒甚至 1 分钟。别所有目标都用同一个频率,浪费资源。

💡 小技巧:如果你的服务是动态的(比如 Kubernetes 里的 Pod),可以用服务发现机制(如 kubernetes_sd_configs)自动发现目标,不用手动维护 targets 列表。

3.3 PromQL 入门:用查询语言说话

PromQL 是 Prometheus 的查询语言。说实话,刚开始看它那套语法,我有点懵。但用多了你会发现,它其实很符合直觉。

咱们从最基础的开始:即时查询范围查询

3.3.1 即时查询

就是查「当前这一刻」的值。比如:

http_requests_total{method="GET"}

返回的是当前时间点,所有 method 为 GET 的请求总数。注意,这里返回的是最新值,不是历史值。

3.3.2 范围查询

如果你想看过去 5 分钟的趋势,就得用范围查询:

http_requests_total{method="GET"}[5m]

这个 [5m] 就是时间范围。返回的是过去 5 分钟内每个采样点的值。你可以用它做聚合、计算速率等。

3.3.3 常用函数

PromQL 内置了很多函数,我挑几个最常用的说说:

函数 作用 示例
rate() 计算每秒增长率(适合 Counter) rate(http_requests_total[5m])
irate() 计算瞬时增长率(更敏感,适合短时间窗口) irate(http_requests_total[1m])
avg_over_time() 计算一段时间内的平均值 avg_over_time(cpu_usage[5m])
sum() 聚合求和 sum(rate(http_requests_total[5m]))
topk() 取前 N 个 topk(3, rate(http_requests_total[5m]))

举个例子,我想看过去 5 分钟每个接口的 QPS:

rate(http_requests_total[5m])

如果我想按 endpoint 分组看:

sum(rate(http_requests_total[5m])) by (endpoint)

嗯,这里要注意:rate() 只能用于 Counter 类型(只增不减的指标)。如果是 Gauge(可增可减),用 avg_over_time() 更合适。

🔑 核心要点:PromQL 的威力在于「标签过滤 + 时间范围 + 聚合函数」的组合。你可以在一个查询里完成多维度的分析,这在传统监控系统里很难做到。

3.4 避坑指南:我踩过的那些坑

最后分享几个我实际踩过的坑,希望能帮你省点时间:

  • 坑一:标签值带空格。PromQL 里标签值如果包含空格,必须用双引号括起来,否则会报错。比如 {method="GET /api"} 是错的,得写成 {method="GET /api"} 但注意,Prometheus 标签值本身不支持空格,所以最好别用。
  • 坑二:忘记加时间范围。用 rate() 时忘了加 [5m],结果返回空。我当时排查了半天,才发现是语法问题。
  • 坑三:指标名大小写。Prometheus 指标名是大小写敏感的。我见过有人把 http_requests_total 写成 Http_Requests_Total,结果查不到数据。

好了,这一章的内容就这些。数据模型、Pull 模型、PromQL 入门,这三块是 Prometheus 的基石。下一章咱们会深入告警规则和 Alertmanager,到时候你会看到 PromQL 的更多妙用。