Skip to content

Prometheus + Grafana 监控 ​

pgAdmin 只能看「此刻」,要看「过去一小时、昨天、上个月」,必须有指标的采集、存储与展示体系。云原生世界为这个问题准备了事实标准组合:Prometheus(采集与存储)+ Grafana(可视化)+ postgres_exporter(数据库侧探针)。它以「拉取(pull)」模型周期性抓取 PostgreSQL 的指标快照,存进时序数据库,再由 Grafana 绘成趋势曲线与告警看板——pgAdmin 上一闪而过的小流水线,在这里被放大成 7×24 的大流水线。

本页完整走一遍这套组合的搭建:postgres_exporter 的部署与账号准备、Prometheus 的抓取配置、自定义查询扩展、Grafana 面板接入,以及一套可以直接抄进生产实践的告警规则示例(连接数、复制延迟、WAL 堆积、缓存命中率、长事务)。学完本页,你的 PostgreSQL 将第一次拥有「历史可回溯、异常会说话」的持续监控能力。

对本节前几页的成果做一次盘点:慢查询日志与 auto_explain 的 JSON 产出可以接入 Grafana 的日志面板(通过 Loki/Elasticsearch 数据源),pg_stat_statements 的口径成为语句级面板的标准语义,pgAdmin 兜底即时确认——本页是这些散装能力的「总装车间」。请带着这个视角阅读后面的配置细节。

前置知识

建议先阅读:pgAdmin 管理与监控

基础概念 ​

三件套的分工 ​

text
┌──────────────┐   HTTP /metrics(拉取)   ┌────────────┐    PromQL     ┌──────────┐
│ PostgreSQL   │◄── postgres_exporter ────│ Prometheus │──────────────►│  Grafana  │
│  (数据库)    │     定期查询 pg_stat_*     │ (采集+存储)│    查询/告警    │ (可视化) │
└──────────────┘                          └────────────┘               └──────────┘
      ▲                                          │
      │ 连接(监控账号)                            │ Alertmanager(可选,告警分发)
      └──────────────────────────────────────────┘
组件职责关键词
postgres_exporter驻留在数据库附近,把 SQL 查询结果翻译成 Prometheus 指标探针 / 转换器
Prometheus周期拉取各 exporter 的 /metrics 端点,存储时序数据,评估告警规则拉取模型 / TSDB
Grafana连接 Prometheus 数据源,渲染面板、看板与告警视图面板 / 看板
Alertmanager(可选)接收 Prometheus 的告警,去重、分组、路由到邮件/IM/Webhook告警分发

术语先建立三个:指标(metric)是带名字与标签的时间序列点;标签(label)给指标附加维度(如 dbname="appdb");PromQL 是 Prometheus 的查询语言,负责聚合、计算速率与触发告警。

与 Zabbix / pgwatch 的定位差异 ​

工具章节的四个方案各有生态位,先立好坐标系,后面两页的展开才不迷路:

方案一句话定位强项成本
Prometheus + Grafana(本页)云原生指标事实标准生态最大、PromQL 灵活、社区看板海量需自建与运维整套栈
Zabbix(下一页)企业级传统监控模板齐全、告警体系成熟、企业特性(用户/审批)重部署重、PromQL 式灵活性弱
pgwatch(17.4.4)PostgreSQL 专用语句级监控开箱即用的语句级面板、preset 体系专注 PG、通用性弱

选型的现实决策树:已有 Zabbix 的企业 → PostgreSQL 接入 Zabbix 而不是另起炉灶;云原生/容器化环境 → Prometheus 体系;想快速获得语句级深度监控 → pgwatch 或商业 SaaS(17.4.5)。多方案并存也是常态:Zabbix 管告警、Prometheus 管看板,关键是不让两套系统的「同一个指标」出现两套口径。

拉取模型对数据库监控的意义 ​

Prometheus 的 pull 模型意味着:监控服务器的故障不会压垮数据库,但数据库的监控盲区会被及时发现(拉不到 = 指标缺失 = 立刻可见)。对数据库监控还有两个具体推论:其一,exporter 与数据库之间保持一个轻量连接即可,监控自身的开销完全可控;其二,抓取间隔(scrape interval)就是你的时间分辨率——15s 间隔可以捕捉到秒级的连接尖峰,1m 间隔则适合容量趋势。间隔的选择是「分辨率 vs 存储」的权衡,没有标准答案。

postgres_exporter 是什么 ​

postgres_exporter 是 Prometheus 官方社区(prometheus-community)维护的 PostgreSQL 指标导出器(前身由个人开发者维护后转入社区)。它内置了针对标准统计视图(pg_stat_database、pg_stat_activity、pg_stat_replication 等)的默认采集,同时支持通过自定义查询文件扩展任意指标——这是它最强大的能力:任何能写成 SQL 的监控问题,都能变成 Grafana 上的曲线。

版本与配置说明

postgres_exporter 经历过配置方式的重大演进:早期版本以 DATA_SOURCE_NAME 环境变量指定连接串;新版本转向配置文件(--config.file 指定 YAML,内含连接串与采集器设置)。两种方式的参数名、指标命名在不同版本间存在差异。本页示例以较新的配置文件方式为主,具体以你所部署版本的官方 README 为准。

语法与用法 ​

准备监控账号 ​

监控账号坚持最小权限,官方推荐授予 pg_monitor 内置角色:

sql
-- 1. 创建监控账号
CREATE ROLE exporter WITH LOGIN PASSWORD '强口令';

-- 2. 授予观察权限(pg_monitor 聚合了多个只读统计角色)
GRANT pg_monitor TO exporter;

-- 3. 允许连接业务库(统计视图大部分在库级)
GRANT CONNECT ON DATABASE appdb TO exporter;

-- 4. 如果要采集 pg_stat_statements(语句级指标),执行其重置函数需要额外授权:
GRANT EXECUTE ON FUNCTION pg_stat_statements_reset() TO exporter;
-- 注意:把 reset 函数交给监控账号意味着"重置"可被误触发,
-- 更稳妥的做法是复用本站 pg_stat_statements 一页的增量快照法,不授权 reset。

同时确认 pg_hba.conf 允许 exporter 主机以口令方式连接:

conf
# pg_hba.conf —— 只放行监控网段,限定账号
host  all  exporter  10.0.8.0/24  scram-sha-256

部署 postgres_exporter ​

bash
# 方式一:二进制部署
wget https://github.com/prometheus-community/postgres_exporter/releases/latest/download/postgres_exporter-*.linux-amd64.tar.gz
tar xzf postgres_exporter-*.tar.gz
cd postgres_exporter-*/
yaml
# queries.yaml —— 自定义查询文件(结构示意,字段以所用版本文档为准)
app_connections:
  query: "SELECT count(*) AS total, count(*) FILTER (WHERE state = 'active') AS active FROM pg_stat_activity WHERE datname = 'appdb'"
  master: true
  metrics:
    - total:
        usage: "GAUGE"
        description: "Total connections to appdb"
    - active:
        usage: "GAUGE"
        description: "Active connections to appdb"
yaml
# exporter 配置文件(新版本方式,示意)
data_source_name: "postgresql://exporter:强口令@10.0.1.12:5432/appdb?sslmode=disable"

# 旧版本方式(环境变量,仍在大量存量部署中使用):
#   DATA_SOURCE_NAME=postgresql://exporter:强口令@10.0.1.12:5432/appdb?sslmode=require
bash
# 启动(含自定义查询文件)
./postgres_exporter --config.file=exporter-config.yml --extend.query-path=queries.yaml

# 验证指标端点
curl -s http://localhost:9187/metrics | head -20
bash
# 方式二:Docker 部署(更常见)
docker run -d --name postgres_exporter \
  -p 9187:9187 \
  -v $PWD/queries.yaml:/etc/postgres_exporter/queries.yaml \
  prometheuscommunity/postgres_exporter

postgres_exporter 的默认端口是 9187,/metrics 端点输出 Prometheus 文本格式指标。部署纪律:exporter 应部署在「与数据库网络可达、与 Prometheus 可达」的位置——常见做法是每台数据库主机一个 exporter(sidecar 模式),或每实例一个。

配置 Prometheus 抓取 ​

yaml
# prometheus.yml
global:
  scrape_interval: 15s          # 抓取间隔 = 时间分辨率
  evaluation_interval: 15s      # 告警规则评估间隔

scrape_configs:
  - job_name: "postgresql"
    static_configs:
      - targets: ["10.0.1.12:9187"]
        labels:
          env: "production"
          instance_role: "primary"
      - targets: ["10.0.1.13:9187"]
        labels:
          env: "production"
          instance_role: "replica"

  # PgBouncer 池化时单独抓取其 exporter
  - job_name: "pgbouncer"
    static_configs:
      - targets: ["10.0.1.12:9127"]
bash
# 加载配置并检查
promtool check config prometheus.yml
curl -s http://prometheus:9090/-/reload    # 需 --web.enable-lifecycle 支持

labels 是指标检索的灵魂:多实例环境务必在抓取配置里打上环境与角色标签,Grafana 面板的「按实例下钻」、告警规则的「只对生产主库生效」都靠标签实现。

接入 Grafana ​

text
1. Grafana → Connections → Data sources → Add Prometheus
   URL: http://prometheus:9090
2. Dashboards → New → Import:
   从 Grafana 官方看板市场搜索 "PostgreSQL"(选择适配 postgres_exporter
   且标明 Prometheus 数据源的看板,按下载量与维护活跃度挑选,
   具体看板编号以市场实际页面为准);
   或手工创建面板(详见下节)。
3. 变量(Variables)建议:datasource、env、instance_role——
   让同一份看板在不同环境间复用。

导入社区看板省时,但理解每个面板背后的 PromQL 更重要——看板作者是别人,告警责任是你。下一节直接手写核心面板。

手工创建核心面板(PromQL 模板) ​

面板PromQL 示意说明
连接数趋势sum by (datname) (pg_stat_activity_count{state="active"})指标名以所用 exporter 版本为准
库大小pg_database_size_bytes直接量测
缓存命中率sum(rate(pg_stat_database_blks_hit{datname="appdb"}[5m])) / (sum(rate(pg_stat_database_blks_hit{datname="appdb"}[5m])) + sum(rate(pg_stat_database_blks_read{datname="appdb"}[5m])))经典差分
事务吞吐rate(pg_stat_database_xact_commit{datname="appdb"}[5m])每秒提交数

指标名的重要提醒

PostgreSQL 的 pg_stat_* 计数器在 exporter 中映射为以 pg_ 开头的指标,精确指标名(下划线、后缀如 _count/_total)随 exporter 版本与采集器开关而变化。写面板前先用 Grafana 的 Explore 浏览实际存在的指标名,或直接 curl exporter:9187/metrics | grep pg_ 查看——不要凭记忆硬编码。

详细说明 ​

默认采集覆盖了什么 ​

postgres_exporter 的内置采集器覆盖数据库运维的主干指标面:

text
实例级:pg_up、启动时长、版本信息
库级:连接数、提交/回滚速率、块命中/读、临时文件、死元组(pg_stat_database)
活动级:当前会话状态分布(pg_stat_activity)
复制级:延迟字节/延迟时长、备库回放状态(pg_stat_replication,主库视角)
后台级:检查点、缓冲区、锁统计(pg_stat_bgwriter/pg_stat_checkpointer 等,随版本调整)

注意「随版本调整」:内核改动了统计视图(如 PG17 拆分 pg_stat_bgwriter),exporter 的内置采集也会跟进调整,跨版本升级 exporter 时要看发行说明。主干指标之外的一切(业务维度、语句级、队列深度),都走自定义查询——这是 exporter 设计哲学的核心:内置管通用,SQL 管一切特殊。

自定义查询:任意 SQL 都能变成曲线 ​

queries.yaml 的能力边界就是 SQL 的能力边界。三个高价值自定义查询方向:

yaml
# 方向一:长事务监控(业务最有感知的问题)
long_transactions:
  query: |
    SELECT count(*) AS count,
           coalesce(max(extract(epoch FROM now() - xact_start)), 0) AS max_age_seconds
    FROM pg_stat_activity
    WHERE state <> 'idle' AND xact_start < now() - interval '5 minutes'
  master: true
  metrics:
    - count:
        usage: "GAUGE"
        description: "Transactions older than 5 minutes"
    - max_age_seconds:
        usage: "GAUGE"
        description: "Age of oldest transaction in seconds"

# 方向二:锁等待数量(对应优化流程值班压缩版第 2 步)
lock_waits:
  query: |
    SELECT count(*) AS waiting
    FROM pg_locks WHERE NOT granted
  master: true
  metrics:
    - waiting:
        usage: "GAUGE"
        description: "Locks waiting to be granted"

# 方向三:按表名的语句扫描统计(对接 pg_stat_statements 自定义聚合)
# 语句级指标建议参考 exporter 仓库自带的 pg_stat_statements 示例查询文件,
# 按需裁剪字段后再上线——全量语句指标的基数(cardinality)控制是关键。

自定义查询的性能红线:每条查询以抓取间隔持续执行,15s 一次的全表扫描也是「每 15 秒一次」。写自定义查询的规矩与写巡检 SQL 相同——只碰统计视图与索引、避免大结果集、必要时加 LIMIT。上线后先在 Grafana 看 exporter 自身耗时与数据库负载确认无副作用。

监控自身开销的评估 ​

数据库人问监控方案的第一句话应该是「你在我库上跑了什么、多久跑一次」。开销账这样算:

text
exporter 的开销 = 抓取间隔内的 SQL 执行成本
  默认采集:十来条轻量查询(全部打在统计视图上),单次 < 数十毫秒
  自定义查询:取决于 SQL 质量(红线已述)
  频率:15s 一次 → 默认采集约占数据库资源的千分之一量级(经验值,仅参考量级)

Prometheus 侧开销:与被监控库无关,在监控服务器上自行消化
时序存储开销:基数 × 采样频率 × 保留期,预算公式见注意事项

两个必须养成的习惯:其一,把 exporter 的连接也纳入 pg_stat_activity 巡检视野——它是库里的常驻客人,行为要可审计;其二,exporter 自身暴露的 /metrics 里包含采集耗时与失败计数,把它们画进 Grafana 的「监控自身健康」面板——监控系统不监控自己,是最隐蔽的盲区。

exporter 的高可用与失效语义 ​

exporter 进程挂了会发生什么?Prometheus 拉取失败,目标标记为 down,pg_up 变 0——上面的 PostgresExporterDown 告警兜的就是这个底。设计上要注意:

text
1. exporter 单实例即可(它只是转换器,无状态),但要被系统服务管理
   (systemd / 容器编排),崩了自动拉起
2. exporter 挂 ≠ 数据库挂,两类告警的 severity 应该不同:
   exporter down → warning(先修监控)
   数据库不可达(exporter 活着但连不上库,或采集错误率飙升)→ critical
3. 网络分区时 pull 模型天然「缺数据」,Grafana 面板要正确显示 gap
   (不要用 fill 插值把故障期画成平线——那是自欺欺人的经典面板事故)

pg_stat_statements 集成:语句级监控上云 ​

exporter 社区提供了 pg_stat_statements 的示例采集查询,把语句级统计搬进时序库。集成后的典型面板:

text
语句级 Top 面板(数据来自 pg_stat_statements 采集):
  total_exec_time 增速 Top 10      → 谁在持续消耗资源
  mean_exec_time 分布              → 尾部延迟画像
  calls 增速 Top 10                → 调用风暴预警

与上一节(慢查询分析)的口径一致,这里的关键是增量思维:PromQL 的 rate()/increase() 函数在两次抓取之间做差分,天然实现了「增量榜单」,无需 reset 与快照表——监控时序库本身就是那个「历史基线表」。语句指标的字段基数大(每模板一个序列),生产部署要控制采集的模板数量(SQL 里加 ORDER BY ... LIMIT),否则时序库会被撑爆。

复制延迟与 WAL 堆积的监控 ​

主备架构下,这两个指标是数据安全的哨兵(完整复制专题见高可用章节):

text
复制延迟(主库视角,pg_stat_replication):
  write_lag / flush_lag / replay_lag    → 时长维度
  pg_wal_lsn_diff(sent_lsn, replay_lsn) → 字节维度

WAL 堆积:
  pg_wal 目录大小 / WAL 段数量
  归档延迟(pg_stat_archiver:failed_count、归档速率)

字节维度的延迟用自定义查询采集(把 pg_wal_lsn_diff 的结果变成 GAUGE),告警阈值按业务容忍度定:流复制场景常见「延迟 > 100MB 或 > 30s 告警」。WAL 堆积告警的优先级要设得很高——它是「备库离线 + 磁盘将满」的双重前兆,历史上很多数据丢失事故的第一信号就是 WAL 目录悄然膨胀。

告警规则示例 ​

Prometheus 的告警规则文件(PromQL 表达,经 Alertmanager 分发)。以下模板覆盖任务点名的五类场景,阈值是示例值,务必按业务校准:

yaml
# postgres-alerts.yml
groups:
  - name: postgresql
    rules:
      # ① 实例不可达(最高优先级)
      - alert: PostgresExporterDown
        expr: pg_up == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "PostgreSQL 指标端点不可达 ({{ $labels.instance }})"

      # ② 连接数逼近上限
      - alert: PostgresTooManyConnections
        expr: sum(pg_stat_activity_count) / pg_settings_max_connections > 0.8
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "连接数已达 max_connections 的 80% ({{ $labels.instance }})"

      # ③ 复制延迟(字节与时长双口径)
      - alert: PostgresReplicationLag
        expr: pg_replication_lag_seconds > 30
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "复制延迟超过 30s ({{ $labels.instance }})"

      # ④ WAL 堆积(自定义查询指标示意)
      - alert: PostgresWalBacklog
        expr: pg_wal_lsn_diff_bytes > 1073741824    # > 1GB
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "WAL 堆积超过 1GB ({{ $labels.instance }})"

      # ⑤ 缓存命中率骤降
      - alert: PostgresCacheHitRatioLow
        expr: |
          sum(rate(pg_stat_database_blks_hit{datname!~"template.*"}[10m]))
          /
          (sum(rate(pg_stat_database_blks_hit{datname!~"template.*"}[10m]))
           + sum(rate(pg_stat_database_blks_read{datname!~"template.*"}[10m]))) < 0.9
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "10 分钟窗口缓存命中率低于 90% ({{ $labels.instance }})"

      # ⑥ 长事务(对接自定义查询 long_transactions)
      - alert: PostgresLongTransaction
        expr: pg_long_transactions_max_age_seconds > 3600    # > 1 小时
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "存在超过 1 小时的长事务 ({{ $labels.instance }})"
bash
# 规则文件检查与加载
promtool check rules postgres-alerts.yml
# 在 prometheus.yml 中引用:
#   rule_files: ["postgres-alerts.yml"]

规则设计的通用原则:for 持续时长过滤抖动(瞬时尖峰不值得半夜打电话);severity 分级路由(critical 打电话、warning 进群);annotations 写清含义与处置入口(告警信息里附 runbook 链接,值班者不用猜)。告警疲劳是监控系统死亡的第一原因——宁可少而准,不可多而麻木。

Grafana 告警与 Prometheus 告警的两条路线 ​

Grafana 自身也演化出了告警能力,两套路线并存时容易混乱:

text
路线 A:Prometheus 规则 → Alertmanager → 分发
  优点:规则与指标同源(Git 管理)、Alertmanager 的分组/静默/抑制成熟
  适合:已有 Prometheus 告警体系、规则较多的团队

路线 B:Grafana Unified Alerting → Grafana 内置分发
  优点:面板即告警、支持多数据源混合查询、配置界面友好
  适合:小团队快速起步、非 Prometheus 数据源也要告警

选一条主路线并写进规范,避免「一半告警在 Alertmanager、一半在 Grafana、职责不清谁也说不准」。起步期用 B 快速验证,规模化了迁到 A 是常见路径;迁移的关键是把每条告警的「表达式 + for + severity + runbook」四要素原样平移,并做一轮并行运行(双路同时触发几天,比对一致性)再下线旧路。

pgbouncer_exporter:把连接池纳入视野 ​

使用 PgBouncer 的架构里,连接池排队是「假慢查询」的头号来源(优化流程页的隐形根因)。独立的 pgbouncer_exporter 把池的统计(SHOW STATS、SHOW POOLS 暴露的容量与等待)转成指标:

yaml
# prometheus.yml 追加
scrape_configs:
  - job_name: "pgbouncer"
    static_configs:
      - targets: ["10.0.1.12:9127"]
text
监控关注点(指标名以 pgbouncer_exporter 文档为准):
  各池的 client 连接数 vs server 连接数 → 池饱和度
  cl_waiting(排队中的客户端)          → 排队即业务延迟
  每池事务/查询速率                     → 与 DB 侧吞吐对账

部署形态与 postgres_exporter 一致:一个池一个 exporter,Prometheus 单独 job 抓取。业务侧耗时高而数据库侧耗时低时,第一时间来这里看排队曲线——优化流程页教的「两端做差」在这里变成两条曲线的叠加图。

与日志、trace 的联动 ​

指标体系有天然盲区:它知道「异常发生了」,不知道「异常现场长什么样」。成熟可观测性方案会让三根支柱互相跳转:

text
Grafana 面板(指标)→ 标注(Annotation)打在曲线上 → 关联日志查询(jsonlog 采集)
                                          → 关联 trace(应用侧 APM)

实操起步:把 PostgreSQL 的变更事件(部署、参数修改、failover)通过 Grafana API 打成 Annotation——曲线上的每一次突变立刻有了「当时发生了什么」的注脚。指标与日志的衔接在日志章节已铺好(jsonlog 采集进 Loki/ES),本页只要求把「跳转入口」建在面板上。

看板设计的四层结构 ​

手工建看板别一张画到底,按「总 → 分 → 深 → 实」分层:

text
第一层 总览(Overview):
  多实例网格:pg_up、连接率、事务速率、复制延迟 —— 一屏判读全部健康度
第二层 单实例详情(Instance Detail):
  连接数、TPS、缓存命中率、临时文件、死元组 —— 点击总览下钻进入
第三层 深度(Deep Dive):
  语句级 Top、锁等待、表 IO、WAL 速率 —— 按需展开的高成本查询
第四层 运维(Ops):
  变更标注、备份产物大小、exporter 自身健康 —— 平时少看,出事必看

四层结构的另一个好处是分工明确:第一层给所有人看,第二层给值班看,第三四层给 DBA 看——看板的使用者不同,颗粒度就该不同。社区看板导入后也可以按这个结构重排,删掉「看起来炫但说不清口径」的面板。

实战示例 ​

示例一:从零搭建最小可用监控 ​

bash
# 1. 数据库侧:建监控账号(见前文 SQL)
# 2. 数据库主机:启动 exporter
docker run -d --name pg_exporter -p 9187:9187 \
  -v /opt/pgmon/queries.yaml:/etc/queries.yaml \
  prometheuscommunity/postgres_exporter
#   连接配置按所用版本方式传入(配置文件或环境变量)

# 3. 监控主机:prometheus.yml 增加抓取(见前文配置),启动 Prometheus
./prometheus --config.file=prometheus.yml

# 4. 验证指标链路
curl -s http://10.0.1.12:9187/metrics | grep pg_up
# 期望输出包含:pg_up 1

# 5. Grafana 添加数据源,创建第一个面板:连接数曲线

全链路 30 分钟。此后数据库的每一次异常波动都会留痕——这是从 pgAdmin 到监控平台的关键一跃。

示例二:连接数告警的完整演练 ​

yaml
# 场景:max_connections=200,演练到 160(80%)时是否触发
- alert: PostgresTooManyConnections
  expr: sum(pg_stat_activity_count) / pg_settings_max_connections > 0.8
  for: 5m
  annotations:
    summary: "连接数 80%:{{ $value | humanizePercentage }} ({{ $labels.instance }})"
    runbook_url: "https://wiki.internal/runbook/pg-connections"
text
演练步骤:
1. 临时把阈值调低到 0.1,确认规则能触发、Alertmanager 能送达(邮件/IM)
2. 恢复阈值 0.8,用压测工具把连接推过 160,观察 5m 的 for 持续期
3. 释放连接,确认告警自动 resolve
4. 演练记录写进 runbook:谁触发、多久送达、误报率如何

没有经过演练的告警规则等于不存在——真正需要它的深夜,它可能根本没配置对。

示例三:用 Grafana 面板回答「昨晚发生了什么」 ​

text
复盘场景:昨夜 23:40 业务接口超时报警,今晨复盘。

1. Grafana 连接数面板:23:35 连接数从 40 跳到 190(逼近上限)
2. 会话状态分布面板:idle 占比暴涨 → 疑似应用连接泄漏
3. 切日志平台(jsonlog 采集),过滤 23:30-23:45:
   大量 connection authorized 记录,无对应 finish
4. 结论:新版本发布引入连接泄漏 → 联动应用侧修复
5. 面板上打 Annotation「v2.4.1 连接泄漏事故」,形成完整证据链

这个复盘演示了指标 + 日志 + 标注的标准三件套——「昨晚发生了什么」从玄学变成五分钟流程。

示例四:多实例环境的三层标签体系 ​

yaml
# 大规模部署的抓取配置(文件发现示例)
scrape_configs:
  - job_name: "postgresql"
    file_sd_configs:
      - files: ["/etc/prometheus/targets/pg/*.json"]
json
// /etc/prometheus/targets/pg/production.json
[
  { "targets": ["10.0.1.12:9187"],
    "labels": { "env": "production", "cluster": "shop", "role": "primary" } },
  { "targets": ["10.0.1.13:9187"],
    "labels": { "env": "production", "cluster": "shop", "role": "replica" } },
  { "targets": ["10.0.2.21:9187"],
    "labels": { "env": "staging", "cluster": "shop", "role": "primary" } }
]
yaml
# 面板与规则按三层标签检索:
#   {env="production"}                    → 只看生产
#   {env="production", cluster="shop"}    → 只看生产 shop 集群
#   {role="replica"}                      → 所有备库

实例超过两位数后,标签体系就是监控系统的骨架——事后补标签的成本远高于开局就定好。

注意事项 ​

监控账号必须有界。 pg_monitor + 库连接权限是全集;pg_stat_statements_reset 等高危函数不建议授予(误触发会清空语句统计基线)。监控账号口令也要轮转,别因为「它只是只读的」就设成永不更换。

exporter 版本与内核版本要一起管理。 内核统计视图的演进(拆分、改名、新增字段)直接影响内置采集器;升级 PostgreSQL 大版本时,exporter 升级要列入同一变更单,升级后核对指标完整性。

自定义查询是双刃剑。 一条写着顺手的全表聚合,会以抓取间隔永续执行。所有自定义查询过三关:只碰统计视图、结果集有界、上线后观察数据库侧负载。exporter 的采集耗时(自监控指标)应纳入巡检。

指标名不要硬编码。 exporter 演进中指标命名有变化(历史上有过较大重构),面板与规则引用的指标名要用「Explore 实测」确认。团队内共享的面板 JSON 要注明适配的 exporter 版本。

抓取间隔决定告警灵敏度的上限。 15s 抓取 + 2m for 的告警,最快也要约 2 分半才触发——秒级故障(如连接风暴打满)可能「还没告警就结束了」。对超快的故障模式,缩短抓取与评估间隔,或改用数据库侧的本地守护(如日志触发)。

单点 Prometheus 不可靠。 监控挂了等于全盲,生产环境至少考虑 Prometheus 的双实例或远程存储(Thanos/VictoriaMetrics/Mimir 等方案选其一),Alertmanager 做集群化。监控自身的可用性是下一层级的运维议题,但不能长期裸奔。

时序库的基数治理。 高基数指标(每语句、每用户、每表一个序列)会在几周内吃掉存储与内存。采集 SQL 里的 LIMIT、Grafana 面板里的标签筛选、定期的基数审计,三件套缺一不可。

不要把 pg_stat_statements 的 mean 当实时指标。 语句统计是累计口径,rate 差分后才有「当前」含义;面板上直接画 mean 曲线会呈现「惯性平线」假象——这也是语句级监控比资源级监控难做的原因。

抓取失败的重试语义要理解。 Prometheus pull 失败不会自动重试本次抓取(下一次抓取按计划进行),指标在时序库里就是缺口。对带宽/抖动敏感的环境,scrape_timeout 要小于抓取间隔(官方默认 10s/15s 的组合即如此),让超时可控而不是悬空。

Secrets 管理别偷懒。 exporter 的连接串里带着数据库口令,无论环境变量还是配置文件方式,都要纳入密钥管理(K8s Secret、Vault、受限权限的配置目录),并禁止把带口令的启动命令写进 shell history 或启动脚本仓库。

最佳实践 ​

  1. 监控账号一律 pg_monitor + 最小补充,口令纳入轮转。 与应用账号严格分离,监控流量在 pg_hba.conf 限定网段。
  2. 每实例一个 exporter,抓取配置用文件发现 + 三层标签(env/cluster/role)。 标签体系从第一个实例就建好,规模化时零迁移成本。
  3. 抓取与评估间隔统一 15s 起步,容量敏感场景再放宽;对告警的「最快触达时间」要有量化预期并写进 SLA。
  4. 五类核心告警先落地:实例存活、连接数、复制延迟、WAL 堆积、长事务,再加缓存命中率与语句级异常。每条规则配 for 持续期与 runbook 链接,每条规则上线前演练。
  5. 自定义查询走「统计视图优先、结果有界、上线观察」三关。 把本站巡检章节的 SQL(长事务、锁等待、膨胀、空闲事务)依次迁入 queries.yaml,让巡检从「人跑」变「系统跑」。
  6. Grafana 看板自建为主、社区看板为辅。 导入市场看板要逐面板理解 PromQL 并本地化指标名;团队共享的面板 JSON 纳入版本管理。
  7. 语句级监控控制基数:采集 SQL 一律 ORDER BY ... LIMIT。 pg_stat_statements 的增量视角(rate/increase)替代「定期 reset」习惯,时序库做历史基线。
  8. 变更事件打 Annotation。 部署、参数修改、failover 全部上曲线注脚,复盘效率翻倍——这是指标体系成本最低、收益最高的增强。
  9. PgBouncer 架构必配 pgbouncer_exporter。 连接池排队是「假慢查询」主源,池的等待队列与数据库指标同图展示。
  10. 监控自身高可用:双 Prometheus 或远程存储,Alertmanager 集群。 全盲的监控系统是最危险的故障状态。
  11. 导出的 Grafana 看板与告警规则进 Git。 像代码一样评审与回滚,杜绝「面板只在某人浏览器里存在」。
  12. 把本页体系与本节前三页打通。 auto_explain 的 JSON 计划进日志管道、慢查询日志的 Top 统计进看板、pg_stat_statements 的口径作为语句面板标准——监控不是工具堆砌,是本节四页工具链的收口;继续深入模板化企业监控请读下一页 Zabbix。

下一节

继续学习:Zabbix 监控

参考链接 ​