Vector 的 postgresql_metrics 源:从 RFC 设计到生产实现的 PostgreSQL 指标采集指南
【免费下载链接】vectorA high-performance observability data pipeline.项目地址: https://gitcode.com/GitHub_Trending/vect/vector
本文以 Vector 仓库中的 RFC 3603(2020-08-27 提交)为骨架,结合仓库中
postgresql_metrics源的最终实现代码与其官方组件文档,完整讲解:Vector 如何原生采集 PostgreSQL 服务器指标、postgresql_metrics源暴露哪些配置项、每一类指标的名称与语义是什么,以及该设计在实现落地过程中的关键演进。读完本文,你将能够独立配置一个postgresql_metrics源,理解其指标命名规则与标签体系,并知道如何用集成测试验证采集结果。
一、RFC 背景:为什么 Vector 需要原生 PostgreSQL 指标源
RFC 3603 的动机非常直接:用户希望采集、转换并转发 PostgreSQL 数据库的性能指标,以更好地观察数据库的运行状态。在此之前,用户若想将 PostgreSQL 指标送入 Vector 管道,只能依赖 Telegraf 的postgresqlinput 或 Prometheus 的postgresql_exporter这类外部采集器,再让 Vector 通过prometheus源去抓取——链路长、组件多、维护成本高。
RFC 的 Scope 明确了两点边界:
- 覆盖:新增一个用于采集 PostgreSQL 服务器指标的 source;
- 不覆盖:其他数据库的指标采集。
之所以值得做,除了 PostgreSQL 本身是被广泛采用的现代数据库、用户普遍需要监控其状态与性能外,更关键的是 Vector 的产品愿景——"One Tool. All Data.",即一个工具完成日志、指标、追踪(当时即将支持)的摄取与投递。RFC 中引用了 Vector 官方文档的两条原则:
One Tool. All Data. —— 一个简单工具将你的日志、指标和追踪从 A 送到 B。
你应该使用 Vector 替代 Logstash、Fluent*、Telegraf、Beats 或类似的工具。
换句话说,把 PostgreSQL 指标采集内建到 Vector 中,是为了减少用户在摄取指标时对第三方采集器的依赖,让 Vector 真正成为可替代 Telegraf 的一体化数据管道。
二、整体设计:一个源、三个查询、一套命名规则
2.1 实现方案:Rust PostgreSQL 客户端直连
RFC 给出的内部方案是:构建一个名为postgresql_metrics(当时名称待确认)的单一 source,使用 Rust 的 PostgreSQL 客户端(tokio_postgres)按配置中给定的地址连接目标数据库服务器。
这一方案在最终实现中完全落地。在 src/sources/postgresql_metrics.rs 中,源码直接依赖tokio_postgres(含Client、Config、NoTls、Row等类型),并通过postgres_openssl::MakeTlsConnector支持 TLS 连接。连接建立后,源码会执行SHOW server_version_num检查服务器版本,只有**主版本号大于等于 9.6(即 server_version_num >= 90600)**的服务器才会被接受,否则抛出InvalidVersion错误——这与 RFC 中"支持所有未 EOL 的 PostgreSQL 版本"的承诺一致。
2.2 三个核心查询
RFC 明确规定了 source 需要执行的三条查询:
SELECT * FROM pg_stat_database SELECT * FROM pg_stat_database_conflicts SELECT * FROM pg_stat_bgwriter最终实现与 RFC 完全一致。在源码中,这三条查询被封装在DatnameFilter与PostgresqlMetrics中:
pg_stat_database(DatnameFilter::pg_stat_database):逐数据库返回连接数、事务提交/回滚、块读写命中、元组增删改查、临时文件、死锁等累计统计;pg_stat_database_conflicts(DatnameFilter::pg_stat_database_conflicts):在备库(standby)上返回因恢复冲突而被取消的查询次数,按冲突类型(表空间、锁、快照、buffer pin、死锁)拆分;pg_stat_bgwriter(DatnameFilter::pg_stat_bgwriter):后台写进程(background writer)的检查点与缓冲写统计,属于集群级(非数据库级)数据。
三者通过try_join_all并发执行(见collect_metrics),采集完成后统计字节数与事件数,并通过内部事件EndpointBytesReceived、EventsReceived上报。采集循环由tokio::time::interval驱动,每次迭代结束发送CollectionCompleted事件,度量单轮采集耗时。
2.3 指标命名规则
RFC 定义的命名规则非常清晰,这也是本文值得记住的核心约定:
table_name_column_name+ Prometheus 结尾(计数器加_total等)
即:表名 + 下划线 + 列名,再依据指标类型追加 Prometheus 风格的_total后缀。RFC 给出的示例:
pg_database_conflicts_confl_tablespace_total
其中pg_database_conflicts是表名,confl_tablespace是列名,_total是因为计数器在 Prometheus 命名约定中必须以total结尾。这套规则与 Prometheus 官方 exporter 的命名方式保持一致,方便用户在 Prometheus 生态中复用查询经验。
需要注意的是,RFC 中"表名"的示例写法是pg_database_conflicts,而最终实现落地时统一采用了pg_stat_database_conflicts_confl_*前缀,指标语义与命名规则本身没有变化,只是前缀与 PostgreSQL 视图名保持了一致。
三、指标清单:完整指标表与语义
RFC 在"Internal Proposal"一节列出了一份完整的指标清单,并标注了每个指标的标签(tag)集:数据库级指标带db、host、server、user,集群级指标带host、server。最终实现保留了这一分层思想,但标签体系简化为:所有指标带endpoint、host,数据库级指标额外带db(server、user未在最终实现中出现,endpoint则是 RFC 在"所有指标将额外带 endpoint(去除用户名/密码后)"一节中要求增加的)。
以下为最终实现实际输出的指标清单,结合 组件文档 中的官方描述整理:
3.1 可用性指标
| 指标名 | 类型 | 标签 | 语义 |
|---|---|---|---|
up | gauge | endpoint, host | PostgreSQL 服务器是否存活,成功采集为1,失败为0 |
up指标对应 RFC 中的pg_up设计("0 表示采集成功、1 表示采集失败"),实现时去掉了pg_前缀并反转语义(成功=1)。在源码collect()中,采集成功时输出gauge!(1.0),任一步骤出错则输出gauge!(0.0)并发出PostgresqlMetricsCollectError内部事件。
3.2 pg_stat_database 系列(数据库级,带 db 标签)
| 指标名 | 类型 | 语义 |
|---|---|---|
pg_stat_database_datid | gauge | 数据库 OID,共享对象为 0 |
pg_stat_database_numbackends | gauge | 当前连接到该数据库的后端进程数(唯一反映当前状态的列) |
pg_stat_database_xact_commit_total | counter | 已提交事务数 |
pg_stat_database_xact_rollback_total | counter | 已回滚事务数 |
pg_stat_database_blks_read_total | counter | 磁盘块读取数 |
pg_stat_database_blks_hit_total | counter | 命中 PostgreSQL 缓冲缓存(非 OS 文件缓存)的块数 |
pg_stat_database_tup_returned_total | counter | 查询返回的行数 |
pg_stat_database_tup_fetched_total | counter | 查询取回的行数 |
pg_stat_database_tup_inserted_total | counter | 插入的行数 |
pg_stat_database_tup_updated_total | counter | 更新的行数 |
pg_stat_database_tup_deleted_total | counter | 删除的行数 |
pg_stat_database_conflicts_total | counter | 因恢复冲突被取消的查询数(仅备库出现) |
pg_stat_database_temp_files_total | counter | 创建的临时文件数 |
pg_stat_database_temp_bytes_total | counter | 写入临时文件的数据总量 |
pg_stat_database_deadlocks_total | counter | 检测到的死锁数 |
pg_stat_database_blk_read_time_seconds_total | counter | 后端读取数据文件块耗时(秒,需启用track_io_timing) |
pg_stat_database_blk_write_time_seconds_total | counter | 后端写入数据文件块耗时(秒,需启用track_io_timing) |
pg_stat_database_stats_reset | gauge | 上次重置统计的时间(Unix 时间戳) |
3.3 pg_stat_database_conflicts 系列(备库冲突,带 db 标签)
| 指标名 | 类型 | 语义 |
|---|---|---|
pg_stat_database_conflicts_confl_tablespace_total | counter | 因表空间被删除而取消的查询数 |
pg_stat_database_conflicts_confl_lock_total | counter | 因锁超时而取消的查询数 |
pg_stat_database_conflicts_confl_snapshot_total | counter | 因旧快照而取消的查询数 |
pg_stat_database_conflicts_confl_bufferpin_total | counter | 因 buffer 被 pin 而取消的查询数 |
pg_stat_database_conflicts_confl_deadlock_total | counter | 因死锁而取消的查询数 |
3.4 pg_stat_bgwriter 系列(集群级,无 db 标签)
| 指标名 | 类型 | 语义 |
|---|---|---|
pg_stat_bgwriter_checkpoints_timed_total | counter | 已执行的定时检查点次数 |
pg_stat_bgwriter_checkpoints_req_total | counter | 已执行的请求式检查点次数 |
pg_stat_bgwriter_checkpoint_write_time_seconds_total | counter | 检查点处理中写入文件的总耗时(秒) |
pg_stat_bgwriter_checkpoint_sync_time_seconds_total | counter | 检查点处理中同步文件的总耗时(秒) |
pg_stat_bgwriter_buffers_checkpoint_total | counter | 检查点期间写入的缓冲数 |
pg_stat_bgwriter_buffers_clean_total | counter | 后台写进程写入的缓冲数 |
pg_stat_bgwriter_maxwritten_clean_total | counter | 后台写进程因写入过多缓冲而停止清理扫描的次数 |
pg_stat_bgwriter_buffers_backend_total | counter | 后端直接写入的缓冲数 |
pg_stat_bgwriter_buffers_backend_fsync_total | counter | 后端自行执行 fsync 的次数 |
pg_stat_bgwriter_buffers_alloc_total | counter | 已分配的缓冲数 |
pg_stat_bgwriter_stats_reset | gauge | 上次重置统计的时间(Unix 时间戳) |
3.5 按版本条件输出的扩展指标(PostgreSQL 12+)
源码中有一处版本分支:当client_version >= 120000(即 PostgreSQL 12 及以上)时,额外采集两个校验和指标:
| 指标名 | 类型 | 语义 |
|---|---|---|
pg_stat_database_checksum_failures_total | counter | 检测到的数据页校验和失败次数(未启用数据校验和时为 0) |
pg_stat_database_checksum_last_failure | gauge | 最后一次数据页校验和失败的时间(Unix 时间戳,未启用时为 0) |
3.6 单位与数值归一化细节
实现层面有几个值得注意的数值处理:
- 时间类指标统一转为秒:
blk_read_time、blk_write_time、checkpoint_write_time、checkpoint_sync_time在 PostgreSQL 中单位为毫秒,源码读取f64后统一除以1000f64再输出,且指标名带有_seconds_段,命名与单位自洽; - 时间戳类指标转为 Unix 时间戳:
stats_reset、checksum_last_failure这类timestamptz列通过chrono::DateTime<Utc>读取后取.timestamp(); - 所有指标使用
MetricKind::Absolute输出,并带namespace(默认postgresql)与当前 UTC 时间戳。
四、配置详解:从 RFC 草案到最终实现
4.1 RFC 中的配置草案
RFC 的 Doc-level Proposal 给出的配置示例如下:
[sources.my_source_id] type = "postgresql_metrics" # required endpoint = "postgres://postgres@localhost" # required - address of the PG server. included_databases = ["production", "testing"] # optional, list of databases to query. Defaults to all if not specified. excluded_databases = [ "development" ] # optional, excludes specific databases. If a DB is excluded explicitly but included in `included_databases` then it is excluded. scrape_interval_secs = 15 # optional, default, seconds namespace = "postgresql" # optional, default is "postgresql", namespace to attach to metrics.RFC 同时提出"还将暴露 HTTP SSL 设置,并在 endpoint URL 中支持ssl参数",并计划补充一份"无需 root 权限运行"的指南。
4.2 最终实现的配置项
对照 生成配置文档 与 源码配置结构体,最终配置形态如下:
[sources.my_source_id] type = "postgresql_metrics" # required endpoints = ["postgresql://postgres:vector@localhost:5432/postgres"] # required include_databases = ["^postgres$", "^vector$"] # optional exclude_databases = ["^template.*"] # optional scrape_interval_secs = 15 # optional, default 15 (seconds) namespace = "postgresql" # optional, default "postgresql" [sources.my_source_id.tls] ca_file = "certs/ca.pem" # optional各配置项说明(RFC 草案 → 最终实现的差异已标注):
| 配置项 | 必填 | 默认值 | 说明 |
|---|---|---|---|
endpoints | 是 | — | RFC 草案为单个endpoint,实现演进为endpoints字符串数组,可同时抓取多个 PostgreSQL 实例,每个元素须为 PostgreSQL 连接 URI 格式(如postgresql://postgres:vector@localhost:5432/postgres) |
include_databases | 否 | 全部数据库 | RFC 草案名为included_databases,实现定稿为include_databases;使用 POSIX 正则表达式匹配datname列,指定""表示包含datname为 NULL 的记录(如共享对象) |
exclude_databases | 否 | 不排除 | RFC 草案名为excluded_databases,实现定稿为exclude_databases;同样使用 POSIX 正则。RFC 明确优先级语义:数据库若同时被 include 与 exclude,则被排除 |
scrape_interval_secs | 否 | 15 | 两次抓取之间的间隔,单位秒 |
namespace | 否 | "postgresql" | 附加到所有输出指标上的命名空间 |
tls.ca_file | 否 | — | 附加 CA 证书文件的绝对路径,证书须为 DER 或 PEM(X.509)格式 |
4.3 include/exclude 的正则过滤是如何实现的
最终实现没有在 SQL 层面硬编码数据库名,而是用DatnameFilter(源码实现)在构建查询时动态拼装 WHERE 子句:
- include 列表中的每个正则生成一个
datname ~ $N谓词(POSIX 正则匹配),多个正则之间用OR连接,整体用括号包裹; - exclude 列表生成
NOT (datname ~ $1 OR datname ~ $2 ...)谓词,并通过AND与 include 组合,从而天然实现"exclude 优先级更高"; ""被特殊处理为datname IS NULL/datname IS NOT NULL条件:include 含空串则保留 NULL 记录,exclude 含空串则剔除 NULL 记录;- 所有正则通过参数化查询(
match_params)传入,避免 SQL 注入。
若include_databases与exclude_databases均未配置,则查询保持最简形式SELECT * FROM pg_stat_database,等价于采集全部数据库。这一点与 RFC 中"未指定时默认采集所有数据库"的语义一致。
4.4 TLS 与连接安全
RFC 提出的 SSL 支持最终以两种方式落地:
- endpoint URL 内建 SSL 参数:连接 URI 中直接支持
sslmode(如?sslmode=require),源码会解析并透传该参数; - 独立
tls配置块:提供tls.ca_file指定附加 CA 证书(DER/PEM 格式),通过 OpenSSL 构建MakeTlsConnector建立加密连接(见源码build_client中的SslConnector::builder(SslMethod::tls_client())与set_ca_file)。
此外,连接成功后源码在 DEBUG 级别记录服务器版本(SELECT version()),并对endpoint标签做了脱敏处理:config_to_endpoint函数会将连接配置重新序列化为不含用户名/密码的规范化 URI(如postgresql:///postgres?host=localhost&port=5432),这正是 RFC 要求的"所有指标均带上去除用户名/密码后的 endpoint 标签"。从源码看,该函数还会保留sslmode、connect_timeout、keepalives_idle、target_session_attrs、channel_binding等连接参数,剔除默认值。
4.5 输出标签体系
最终实现的标签体系如下:
- 所有指标:
endpoint(脱敏后的连接 URI)、host(取自连接配置的主机,TCP 主机名或 Unix socket 路径); - 数据库级指标(pg_stat_database 与 pg_stat_database_conflicts 系列):额外带
db标签(取自datname列,NULL 时为""); - 集群级指标(pg_stat_bgwriter 系列):仅
endpoint与host。
五、权限要求与运维注意
5.1 所需权限
组件文档 的 "Required Privileges" 一节明确指出:postgresql_metrics通过向配置的 PostgreSQL 服务器发起查询来采集指标,必须确保配置的用户对以下三个视图拥有 SELECT 权限:
pg_stat_databasepg_stat_database_conflictspg_stat_bgwriter
RFC 也提到要补充"无需 root 权限运行"的指南——即使用具备上述视图查询权限的专用低权限账号,而不是超级用户。实际操作中应避免在 endpoint URI 中硬编码高权限账号口令,可通过 Vector 的密钥管理能力注入凭据。
5.2 采集行为与版本限制
- 多实例并发:
endpoints中每个实例对应一个独立的PostgresqlMetrics采集器,抓取时通过join_all并发执行,互不阻塞; - 版本校验:连接时执行
SHOW server_version_num,低于 9.6 的服务器连接会被拒绝并返回InvalidVersion错误;12.0 以上自动多采集两个校验和指标; - 失败语义:任何一轮采集中有实例失败,该实例输出
up = 0,成功实例正常输出,管道不会中断; - 内部遥测:每轮抓取会产生
collect_completed_total、collect_duration_seconds等内部指标,可用于监控采集器自身健康状况。
六、集成测试验证:实现确实按 RFC 工作
postgresql_metrics的集成测试位于 src/sources/postgresql_metrics.rs 的integration_tests模块(需启用postgresql_metrics-integration-testsfeature),测试覆盖了 RFC 中的核心承诺:
- test_host / test_local:分别验证 TCP 连接与 Unix socket 连接(
postgresql:///postgres?host=<socket>&user=vector&password=vector); - test_host_ssl:验证
?sslmode=require+tls.ca_file的加密连接; - test_host_include_databases:配置
["^vec", "gres$"],断言所有指标的db标签只能是vector或postgres; - test_host_exclude_databases:配置排除
["^vec", "gres$"],断言没有任何指标带vector/postgres的db标签; - test_host_exclude_databases_empty:验证排除
""(即排除datname IS NULL的记录); - test_host_include_databases_and_exclude_databases:include
template\d+且 excludetemplate0时,断言只保留template1—— 直接验证了 RFC 中"被显式排除的数据库即使被 include 也会被排除"的优先级规则。
测试同时断言:事件数大于 1、up指标值为 1、所有指标 namespace 为postgresql、每条指标都带endpoint与host标签,且pg_stat_database_datid、pg_stat_database_conflicts_confl_tablespace_total、pg_stat_bgwriter_checkpoints_timed_total三类来源的指标都存在——三条查询全部生效。
七、设计决策回顾:Prior Art、替代方案与取舍
7.1 已有实现参考(Prior Art)
RFC 列出并参考了社区已有的 PostgreSQL 采集实现:wrouesnel 的postgres_exporter、Telegraf 的postgresqlinput 插件,以及 collectd 的 PostgreSQL 插件。这些项目验证了"通过查询统计视图采集指标"这一路线的可行性,Vector 的指标命名也向其靠拢。
7.2 替代方案:外部采集器 + prometheus 源
RFC 认真考虑过一个替代方案:不新增 source,而是让用户运行 Telegraf 的 postgresql input 或 Prometheus 的 postgresql_exporter,再由 Vector 的prometheus源抓取结果。该方案能复用既有项目,但 RFC 明确否决了它,理由是这与 Vector 的"One Tool. All Data."原则相悖——Vector 的目标是替代 Telegraf,而不是依赖它。不过 RFC 也承认:已经在运行 Telegraf 或 PostgreSQL Exporter 的用户,完全可以继续走这条老路,prometheus源依然可用。
7.3 缺点与未来工作
RFC 坦承该方案的缺点主要是新增 source 带来的额外维护与集成测试负担(因此仓库中配套了完整的集成测试矩阵)。RFC 还留下两个待办:
- Outstanding Questions:实现过程中应关注是否采集
pg_settings; - Future Work:后续可扩展采集更多数据库指标,包括复制(Replication)指标、锁(Locks)指标、pg_stat_user_tables。
八、总结
从 RFC 3603 到落地,postgresql_metrics源完整兑现了设计承诺:以 Rust 客户端直连 PostgreSQL、执行三条核心统计视图查询、按"表名_列名 + Prometheus 后缀"命名指标、以endpoint/host/db标签描述指标归属。与 RFC 草案相比,实现的演进主要体现在:endpoint升级为多实例的endpoints数组、include/exclude 从字面匹配升级为 POSIX 正则、指标命名统一为pg_stat_*前缀、新增 PostgreSQL 12+ 校验和指标、以及 endpoint 标签的凭据脱敏处理。
如果你正在用 Vector 构建可观测性管道,只需在配置中加上一个postgresql_metrics源,就能以极低代价把 PostgreSQL 的事务、缓冲、检查点、冲突等关键性能信号汇入统一的日志/指标管道,再配合 Vector 的 transform 与 sink 能力完成富化与分发。相关参考材料:配置结构体见 src/sources/postgresql_metrics.rs、采集逻辑见同文件collect_pg_stat_database等函数、指标语义与权限要求见 组件文档。
【免费下载链接】vectorA high-performance observability data pipeline.项目地址: https://gitcode.com/GitHub_Trending/vect/vector
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考