暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

pgdog 从原理到实践

原创 岳麓丹枫 3天前
64

Table of Contents

pgdog 从原理到实践

基于 openEuler 22.03 LTS-SP4 (aarch64, 8核) 环境实战整理

一、pgdog 原理篇

1.1 架构概览

pgdog 是 PostgreSQL 生态中功能最全面的连接池、代理、负载均衡器和查询路由器,使用 Rust + Tokio 异步框架构建,核心能力矩阵:

能力 说明
连接池 支持 transaction / session 两种池化模式,将数千客户端连接收敛为少量后端连接
读写分离 基于 PgQuery (libpg_query) 对每条 SQL 做 AST 解析,自动识别读写,路由到正确角色节点
负载均衡 支持 random / round_robin / least_active_connections / weighted_round_robin
分片 数据库级水平分片,透明路由查询到正确分片,支持 resharding
查询缓存与重写 prepared statement 缓存、查询结果重写
健康检查 实时检测后端健康状态,自动剔除/恢复故障节点
故障切换 role=auto 自动感知主备切换,配合 Patroni 实现零停机
多租户 支持 multi_tenant 隔离,不同用户/database 的池完全独立

架构层级关系

客户端 (psql / pgbench / App) │ ▼ pgdog (0.0.0.0:6432) ← Tokio 异步运行时 (workers 个线程) ├── Frontend 层:接收客户端连接、协议解析、认证 ├── Query Parser 层:PgQuery AST 解析 + 查询缓存 ├── Router 层:读写分离决策、分片路由 ├── Pool Manager 层:连接池管理、健康检查、统计 └── Backend 层:与 PostgreSQL 后端连接管理 │ ▼ PostgreSQL (127.0.0.1:5433) ├── database 1: db_biz_01 ├── database 2: db_biz_02 ├── ... └── database N: db_biz_40

1.2 请求处理链路

一个客户端查询经过 pgdog 的完整链路:

客户端连接 │ ├─► [Frontend] 接收连接 → 协议握手 (SSL/StartupMessage) │ │ ├─► [Auth] 认证检查:查 users.toml → SCRAM/MD5/Trust 验证 │ │ ├─► [Router] StartupMessage 提取 database + user │ │ → 查找对应连接池 (user, database) 组合 │ │ → 若不存在 → 自动创建或报错 │ │ ├─► [Pool] 从 (user, database) 池获取/创建后端连接 │ │ → 池满:等待 checkout_timeout → 超时报错 │ │ → 有可用连接:取出 → 健康检查通过 → 分配 │ │ ├─► [Parser] 对每条 SQL 做 AST 解析 (libpg_query) │ │ → 识别 DML/DQL → 决定 read/write │ │ → 提取表名 + WHERE 条件 → 分片路由决策 │ │ ├─► [Router] 根据解析结果: │ │ → Write → 路由到 primary │ │ → Read → 路由到 replica (负载均衡策略) │ │ ├─► [Execute] 转发查询到后端 PG 连接 │ │ → 等待结果 → 回传给客户端 │ │ └─► [Release] 事务结束(pooler_mode=transaction) 或 会话结束(pooler_mode=session) → 归还连接到池 → 执行连接状态清理机制

连接状态清理可能包括:

  • RESET ALL

  • DEALLOCATE ALL

  • DISCARD ALL

  • 其他内部状态清理逻辑

具体行为取决于 pgdog 当前版本实现。

1.3 连接池模式详解

pgdog 的连接池以 (user, database_name) 为粒度——同一池内按 role 区分 primary/replica 连接。实际使用中最关键的是 pooler_mode

Transaction 模式(默认推荐)

客户端 A: BEGIN → SELECT → UPDATE → COMMIT │ │ ▼ ▼ 后端连接 1: [被 A 独占...] → 归还到池 → 执行连接状态清理 │ 客户端 B: ──► 获取同一个后端连接 → BEGIN → SELECT → COMMIT 特点: • 事务结束后立即归还连接,连接复用率高 • 连接归还池时会执行状态清理机制,确保后续客户端复用时不会继承前一个客户端的 session 状态 • 具体清理方式取决于 pgdog 版本和配置,可能采用 DISCARD ALL 或等价处理 • 适合:短连接、PHP/Web 应用、无状态 API • **支持 prepared statement**:pgdog 在 extended 模式下负责管理协议级 prepared statement 生命周期 • 限制:依赖 session 生命周期的状态无法跨事务保持,例如 SET、临时表、LISTEN/NOTIFY 等 • ⚠️ **必须注意 role 配置**:单主库场景必须用 `role = "primary"`,`role = "auto"` 依赖 pgdog 的角色检测机制,如果配置不完整可能导致 checkout timeout(见 7.7)

Session 模式

客户端 A: 连接建立 → 分配专属后端连接 客户端 B: 连接建立 → 分配专属后端连接 特点: • 客户端整个会话独占一个后端连接 • 支持所有 PG 特性(SET、LISTEN/NOTIFY、cursor) • 客户端可直接使用 PG 原生 prepared statement(连接独占,状态持久) • pgdog 的 prepared statement 代理/缓存机制自动禁用(源码逻辑),直通后端 • 适合:长连接、有状态应用、需要 session 级别状态 • 限制:连接复用率为零,需要大量后端连接

⚠️ 源码中的关键逻辑pgdog-config/src/core.rs):

// Session 模式下自动禁用 prepared_statements 代理 pub fn prepared_statements(&self) -> PreparedStatements { if self.config.general.pooler_mode == PoolerMode::Session { PreparedStatements::Disabled } else { self.config.general.prepared_statements } }

注意:Session 模式禁用的是 pgdog 的 prepared statement 代理/缓存机制(即 pgdog 不再在中间层管理 Parse/Bind/Execute 的映射),而非禁止客户端使用 prepared statement。由于 Session 模式下连接是独占的,客户端可以直接与 PG 建立 prepared statement,状态在会话内持久。

1.4 用户认证与 all_databases 机制

pgdog 的用户认证有两层

说明 配置位置
客户端 → pgdog auth_type 控制 (scram/md5/trust) pgdog.toml [general]
pgdog → 后端 PG server_auth 控制 (password/rds_iam/vault) users.toml [users]

说明server_auth = "password" 表示"使用 users.toml 中的 server_password 静态密码"进行后端认证,与 auth_typemd5/scram/trust(认证协议)不是同一层面的概念。

注意server_auth = "password" 表示 pgdog 使用配置文件中的静态密码连接 PostgreSQL。它不是 PostgreSQL authentication method 中的 password/md5/scram 类型。两者属于不同层:

all_databases 机制(源码 pgdog-config/src/users.rs):

User 结构体有三个互斥的数据库绑定方式:

字段 类型 说明 优先级
database String 绑定单个数据库 最低,与 databases 互斥
databases Vec<String> 绑定多个指定数据库
**all_databases** **bool** 绑定 pgdog.toml 中所有数据库 最高

源码中的冲突检测逻辑(Users::check()):

if user.all_databases && (!user.databases.is_empty() || !user.database.is_empty()) { warn!("user is configured for all databases and a specific database, defaulting to all databases"); }

工作流程:pgdog 启动/RELOAD 时,对所有 all_databases = true 的用户,自动为 Config 中的每个数据库生成一份克隆用户配置:

// databases.rs:640-669 (简化) if user.all_databases { config.databases.keys() .map(|database| { let mut user = user.clone(); user.database = database; user }) .collect() }

这意味着:一条 [[users]] + all_databases = true = 自动为每个 [[databases]] 创建独立的 (user, database) 连接池。

1.5 配置优先级体系

pgdog 的配置项有三个级别,从高到低:

per-user 级别 (users.toml [[users]]) │ 覆盖 ▼ per-database 级别 (pgdog.toml [[databases]]) │ 覆盖 ▼ global 级别 (pgdog.toml [general])

可被覆盖的参数包括:

参数 global per-database per-user
pool_size default_pool_size pool_size pool_size
min_pool_size min_pool_size min_pool_size min_pool_size
pooler_mode pooler_mode pooler_mode pooler_mode
idle_timeout idle_timeout idle_timeout idle_timeout
statement_timeout statement_timeout statement_timeout
lock_timeout lock_timeout lock_timeout
read_only read_only read_only
server_lifetime server_lifetime server_lifetime server_lifetime

二、配置项详解篇

以下配置项基于 pgdog 源码中 GeneralDatabaseUser struct 的完整字段,含默认值和说明。

2.1 pgdog.toml — [general] 全量配置项

2.1.1 监听与运行时

配置项 类型 默认值 说明 热重载
host String 0.0.0.0 监听地址。绑定所有网卡或指定 IP
port u16 6432 监听端口
workers usize 2 Tokio 异步工作线程数。建议 CPU 核数 × 0.5~1,多核系统建议每 2 个 vCPU 配 1 个 worker

workers 调优:8 核机器建议 4~80 表示使用单线程 current_thread runtime(仅调试场景)。

2.1.2 连接池核心参数

配置项 类型 默认值 说明
default_pool_size usize 10 每个 (user,db) 池的最大后端连接数。强烈建议远低于 PG max_connections,为运维预留连接
min_pool_size usize 1 每个池最小保持连接数。启动时即创建,减少首次请求延迟
pooler_mode PoolerMode transaction transaction:事务结束归还;session:会话结束归还

注意default_pool_size 也可写作 max_pool_size(别名,源码兼容)。

2.1.3 认证

配置项 类型 默认值 说明
auth_type AuthType scram 客户端认证方式:scram / md5 / trust
passthrough_auth PassthroughAuth disabled 认证透传:客户端密码直传给后端 PG。enabled 需 TLS,enabled_plain 无需 TLS。启用后 users.toml 中的 password 可为空

2.1.4 超时控制(单位:毫秒)

⚠️ 重要:pgdog 的超时参数类型为 u64(毫秒整数),不支持 "5s" / "10m" 等时间单位字符串。这与 PgBouncer 不同,详见 7.1 超时参数不支持时间单位字符串

配置项 类型 默认值 说明
connect_timeout u64 5_000 创建后端连接超时 (ms)
connect_attempts u64 1 连接重试次数
connect_attempt_delay u64 0 重试间隔 (ms)
checkout_timeout u64 5_000 池满时客户端等待获取连接的超时 (ms)
idle_timeout u64 60_000 超过 min_pool_size 的空闲连接存活时间 (ms)
client_idle_timeout u64 MAX (无限) 客户端空闲超时 (ms),超时断开
client_idle_in_transaction_timeout u64 MAX (无限) 客户端事务中空闲超时 (ms)
client_login_timeout u64 60_000 客户端登录认证超时 (ms)
query_timeout u64 MAX (无限) 单条查询最大执行时间 (ms)
rollback_timeout u64 5_000 连接归还时 ROLLBACK 超时 (ms)
server_lifetime u64 86_400_000 后端连接最大存活时间 (ms),默认 24 小时。到期归还池时关闭
server_lifetime_jitter u64 0 server_lifetime 抖动范围 (ms),防止集中过期
shutdown_timeout u64 60_000 关闭时等待活跃事务完成的超时 (ms)
shutdown_termination_timeout Option<u64> None shutdown_timeout 过期后强制终止的超时 (ms)。设置后 pgdog 会向 PG 发送 CANCEL 请求

2.1.5 健康检查

配置项 类型 默认值 说明
healthcheck_interval u64 30_000 健康检查间隔 (ms)
healthcheck_timeout u64 5_000 健康检查超时 (ms)
idle_healthcheck_interval u64 30_000 空闲连接健康检查间隔 (ms)
idle_healthcheck_delay u64 5_000 启动后延迟空闲健康检查的时间 (ms)
healthcheck_port Option<u16> None 负载均衡器 HTTP 健康检查端口
ban_timeout u64 300_000 故障封禁自动解封时间 (ms),默认 5 分钟
ban_replica_lag u64 MAX (不限制) 副本延迟超过此毫秒数即封禁
ban_replica_lag_bytes u64 MAX (不限制) 副本延迟超过此字节数即封禁

2.1.6 读写分离与负载均衡

配置项 类型 默认值 说明
load_balancing_strategy enum random random / round_robin / least_active_connections / weighted_round_robin
read_write_strategy enum conservative conservative:显式事务路由到主库;aggressive:检查事务第一条语句决定
read_write_split enum include_primary include_primary:主库也分担读;exclude_primary:主库只写;prefer_primary:读请求优先路由到主库,主库压力大时自动分流到副本

2.1.7 查询解析

配置项 类型 默认值 说明
prepared_statements enum extended disabled / extended / full。Session 模式下代理机制自动禁用
prepared_statements_limit usize MAX (无限制) 每服务端连接最大 prepared statement 数
query_parser QueryParserLevel auto 查询解析器:auto / on / off
query_parser_engine enum pg_query 底层解析引擎(基于 libpg_query)
query_cache_limit usize 1_000 语句缓存大小
query_size_limit Option<usize> None 查询消息大小限制 (bytes)
query_size_limit_action enum warn 超限动作:warn 记录日志 / block 拒绝并断开
log_min_duration_parse Option<u64> None 解析耗时超过此值记录 WARN 日志 (ms)
log_query_sample_length usize 1000 日志中查询文本最大字符数

2.1.8 日志与监控

配置项 类型 默认值 说明
log_level String info (from RUST_LOG) 日志级别,同 RUST_LOG 语法
log_format enum text text / json / json_flattened
log_connections bool true 记录客户端连接
log_disconnections bool true 记录客户端断开
log_dedup_window u64 0 (关闭) 日志去重窗口 (ms)
log_dedup_threshold u64 0 (关闭) 窗口内允许的重复日志数
openmetrics_port Option<u16> None Prometheus metrics 端口
openmetrics_namespace Option<String> None metrics 前缀
stats_period u64 15_000 SHOW STATS 和 Prometheus 指标统计周期 (ms)

2.1.9 TLS

配置项 类型 默认值 说明
tls_certificate Option<PathBuf> None TLS 证书路径
tls_private_key Option<PathBuf> None TLS 私钥路径
tls_client_required bool false 拒绝非 TLS 连接
tls_verify TlsVerifyMode prefer 后端 TLS 验证:prefer / verify_full / disabled
tls_server_ca_certificate Option<PathBuf> None 验证后端证书的 CA 证书
tls_client_ca_certificate Option<PathBuf> None 验证客户端证书的 CA 证书

2.1.10 连接恢复

配置项 类型 默认值 说明
connection_recovery enum recover 后端连接恢复策略:recover / drop
client_connection_recovery enum drop 客户端连接恢复策略
dry_run bool false 单分片部署下启用查询解析器并记录决策,不实际路由

2.1.11 高级特性

配置项 类型 默认值 说明
two_phase_commit bool false 跨分片写事务启用两阶段提交
two_phase_commit_auto Option<bool> None 自动将单语句写事务转为 2PC
two_phase_commit_wal_dir Option<PathBuf> ./pgdog_wal 2PC WAL 目录
cross_shard_disabled bool false 全局禁用跨分片查询
dns_ttl Option<u64> None DNS 记录 TTL 覆盖 (ms)
pub_sub_channel_size usize 0 (关闭) pub/sub 后台任务队列大小,>0 启用
lsn_check_interval u64 5_000 LSN 复制延迟检查间隔 (ms)
lsn_check_timeout u64 5_000 LSN 检查超时 (ms)
lsn_check_delay u64 MAX (关闭) LSN 检查延迟启动。设为 0 表示启动后立即开始 LSN 检查(启用自动故障切换检测)
mirror_queue usize 128 镜像数据库队列大小
mirror_exposure f32 1.0 镜像流量占比 (0.0~1.0)
query_log Option<PathBuf> None 查询日志文件路径(生产慎用,性能开销大
query_log_stdout bool false 查询日志输出到标准输出

2.2 pgdog.toml — [admin] 段

[admin] name = "admin" # 管理数据库名(默认 admin) user = "admin" # 管理用户名(默认 admin) password = "..." # 管理密码(必须用明文,不支持 SCRAM 哈希) # 不设置则随机生成 _pgdog_XXXXXXXXXXXX # debug 模式下启动日志会打印密码

注意

2.3 pgdog.toml — [[databases]] 段

配置项 类型 默认值 说明
name String 必填 客户端连接时使用的数据库名
host String 必填 后端 PG 地址
port u16 5432 后端 PG 端口
role enum primary primary / replica / auto
shard usize 0 分片编号(从 0 开始)
database_name Option<String> None 后端实际数据库名(与 name 不同时指定)
user Option<String> None 后端连接用户(覆盖 users.toml 的 server_user)
password Option<String> None 后端连接密码(覆盖 users.toml 的 server_password)
pool_size Option<usize> None 覆盖 default_pool_size
min_pool_size Option<usize> None 覆盖全局 min_pool_size
pooler_mode Option<PoolerMode> None 覆盖全局 pooler_mode
statement_timeout Option<u64> None 连接级 statement_timeout (ms)
lock_timeout Option<u64> None 连接级 lock_timeout (ms)
idle_timeout Option<u64> None 覆盖全局 idle_timeout (ms)
read_only Option<bool> None 强制连接为只读模式
server_lifetime Option<u64> None 覆盖全局 server_lifetime (ms)
server_lifetime_jitter Option<u64> None 覆盖全局 server_lifetime_jitter (ms)
lb_weight u8 255 加权负载均衡权重 (0~255)
resharding_only bool false 仅用于 resharding,不服务常规流量

2.4 users.toml — [[users]] 段

配置项 类型 默认值 说明
name String 必填 用户名
database String "" 绑定单个数据库
databases Vec<String> [] 绑定多个数据库
**all_databases** **bool** **false** 绑定所有数据库(优先级最高)
password Option<String> None 客户端认证密码(必须用明文,不支持 SCRAM 哈希
passwords Vec<String> [] 多个密码(全部尝试)
password_hash Option<String> None 密码哈希(不存明文密码,配合 server_auth 使用)
pool_size Option<usize> None 覆盖 default_pool_size(per-user 最高优先级)
min_pool_size Option<usize> None 覆盖全局 min_pool_size
pooler_mode Option<PoolerMode> None 覆盖全局 pooler_mode
server_user Option<String> None 后端 PG 连接用户名(默认同 name)
server_password Option<String> None 后端 PG 连接密码(默认同 password)
server_auth ServerAuth password 后端认证方式:password(使用 server_password 静态密码)/ rds_iam / azure_workload_identity / vault_dynamic / vault_static
server_iam_region Option<String> None RDS IAM 区域
server_vault_path Option<String> None Vault credentials 路径
vault_path Option<String> None 用于验证客户端密码的 Vault static role 路径
vault_refresh_percent Option<u8> None Vault 凭证刷新阈值百分比 (1~80)
identity Option<String> None mTLS 用户身份
statement_timeout Option<u64> None 连接级 statement_timeout (ms)
lock_timeout Option<u64> None 连接级 lock_timeout (ms)
idle_timeout Option<u64> None 覆盖全局 idle_timeout (ms)
read_only Option<bool> None 强制只读
replication_mode bool false 启用 replication=database 连接参数
replication_sharding Option<String> None 复制分片目标数据库
cross_shard_disabled Option<bool> None 禁用跨分片查询
two_phase_commit Option<bool> None 覆盖全局 2PC 设置
two_phase_commit_auto Option<bool> None 覆盖全局 2PC auto 设置
schema_admin bool false 拥有 DDL 提升权限的 schema 管理员
server_lifetime Option<u64> None 覆盖全局 server_lifetime (ms)
server_lifetime_jitter Option<u64> None 覆盖全局 jitter (ms)

三、最佳实践案例篇

3.1 案例一:多库多用户连接池(40业务库 + 系统库)

场景

  • 1 个 PG 实例 (127.0.0.1:5433),40 个业务库 + 3 个系统库:

    • postgres

    • template0

    • template1

  • 所有库使用同一个用户 postgres,SCRAM-SHA-256 认证

  • 8 核服务器

说明:template0 通常不会作为业务连接目标,因此实际管理库数量根据环境可能不同。

pgdog.toml

[general] host = "0.0.0.0" port = 6432 # 连接池核心 default_pool_size = 10 # 每 (user,db) 池上限 # 计算:1 用户 × 42 库 × 10 = 最多 420 连接 min_pool_size = 2 # 每池 2 个预热连接 pooler_mode = "transaction" # 事务级复用 # 认证(生产环境) auth_type = "scram" # 推荐使用 SCRAM,具体根据客户端兼容性配置 connect_timeout = 5000 # 5s checkout_timeout = 5000 # 5s idle_timeout = 600000 # 10min client_idle_timeout = 1800000 # 30min(set 0 for unlimited) query_timeout = 60000 # 60s server_lifetime = 86400000 # 24h # 性能调优(8核) workers = 6 load_balancing_strategy = "least_active_connections" prepared_statements = "extended" # 健康检查 healthcheck_interval = 30000 healthcheck_timeout = 5000 ban_timeout = 60000 # 1min 自动解封 # 日志与监控 log_format = "json" log_level = "info" log_connections = true log_disconnections = true openmetrics_port = 9090 [admin] name = "admin" user = "admin" password = "your_strong_admin_password" # === 系统库 === [[databases]] name = "postgres" host = "127.0.0.1" port = 5433 role = "primary" [[databases]] name = "template1" host = "127.0.0.1" port = 5433 role = "primary" # === 40 个业务库 === # 替换 db_biz_NN 为实际库名 [[databases]] name = "db_biz_01" host = "127.0.0.1" port = 5433 role = "primary" # ... db_biz_02 ~ db_biz_39 ... [[databases]] name = "db_biz_40" host = "127.0.0.1" port = 5433 role = "primary"

users.toml

# all_databases = true 一条记录覆盖所有数据库 [[users]] name = "postgres" all_databases = true password = "Admin123@" # 明文密码(当前版本 password 字段不支持 SCRAM 哈希)

关键决策理由

决策 原因
default_pool_size = 10 1 用户 × 42 库 × 10 = 420 连接,可控
min_pool_size = 2 42 × 2 = 84 个预热连接,冷启动延迟消除
pooler_mode = "transaction" 绝大多数应用场景最佳选择
all_databases = true 1 条 users 记录,新增库无需改 users.toml
auth_type = "scram" 生产环境推荐,安全性更高;trust 仅适用于明确隔离环境
workers = 6 8 核 × 0.75
role = "primary" 单主库场景必须,role = "auto" 会导致 checkout timeout(见 7.7)

PG 端配合

-- 理论最大 backend connection: -- pool_size × user_count × database_count ALTER SYSTEM SET max_connections = 600; -- 420 + 预留 ALTER SYSTEM SET idle_in_transaction_session_timeout = '10min'; SELECT pg_reload_conf();

注意:

上述公式表示理论最大连接数量,并不代表 pgdog 启动时一定创建全部连接。

实际 PostgreSQL backend connection 数量还受到:

  • min_pool_size

  • 当前活跃事务数量

  • 实际访问数据库数量

  • 连接生命周期策略

影响。

例如:

1 用户 42 数据库 pool_size = 10 理论最大: 1 × 42 × 10 = 420 但是: min_pool_size = 2 初始连接可能: 42 × 2 = 84

因此生产环境不能简单按照理论最大值配置,而应该结合业务峰值压测。

3.2 案例二:读写分离

场景:一主两从,读请求自动路由到副本,写请求路由到主库。

[general] host = "0.0.0.0" port = 6432 default_pool_size = 50 pooler_mode = "transaction" load_balancing_strategy = "least_active_connections" # 最少连接优先 read_write_strategy = "conservative" # 事务级判断 read_write_split = "exclude_primary" # 主库不承担读 # 主库 [[databases]] name = "prod" host = "10.0.0.1" port = 5432 role = "primary" # 副本 1 [[databases]] name = "prod" host = "10.0.0.2" port = 5432 role = "replica" # 副本 2 [[databases]] name = "prod" host = "10.0.0.3" port = 5432 role = "replica"

关键:多个 [[databases]] 使用相同 name,pgdog 自动为 name="prod" 聚合成一个集群。role 决定读写路由。

策略对比

read_write_split 读查询路由 适用场景
include_primary 主库 + 所有副本 主库资源充足
exclude_primary 仅副本 主库压力大、写为主
prefer_primary 优先主库,压力大时自动分流到副本 保守策略

3.3 案例三:自动故障切换

场景:配合 Patroni 等 HA 方案,pgdog 自动感知主备切换。

[general] # 启用 LSN 检查以自动检测 primary/replica 角色 lsn_check_delay = 0 # 0 = 启动后立即开始 LSN 检查,无延迟 lsn_check_interval = 5000 # 5s lsn_check_timeout = 5000 # 5s # 副本延迟超阈值自动封禁 ban_replica_lag = 10000 # 10s 延迟即封禁 ban_replica_lag_bytes = 10485760 # 10MB 延迟即封禁 [[databases]] name = "prod" host = "10.0.0.1" port = 5432 role = "auto" # pgdog 自动检测角色 [[databases]] name = "prod" host = "10.0.0.2" port = 5432 role = "auto"

原理role = "auto" 时 pgdog 通过 pg_is_in_recovery() 动态判断节点是 primary 还是 replica,主备切换后自动更新路由。⚠️ 陷阱role = "auto" 必须配合 lsn_check_delay = 0 使用。如果 LSN 检查被禁用(默认 lsn_check_delay = MAX),role = "auto" 会导致 checkout timeout。详见 7.7

3.4 案例四:分片集群

场景:表 orderscustomer_id 分片到 2 个节点。

[[databases]] name = "sharded_db" host = "10.0.0.1" port = 5432 database_name = "shard_0" shard = 0 role = "primary" [[databases]] name = "sharded_db" host = "10.0.0.2" port = 5432 database_name = "shard_1" shard = 1 role = "primary" # 分片表定义 [[sharded_tables]] database = "sharded_db" name = "orders" column = "customer_id" data_type = "bigint" # 分片映射 [[sharded_tables.mapping]] values = [1, 2, 3, 4, 5] shard = 0 [[sharded_tables.mapping]] values = [6, 7, 8, 9, 10] shard = 1

对于包含明确分片键条件的简单查询,例如:pgdog 可以根据分片规则计算目标 shard。

注意:

复杂 SQL,例如:

  • JOIN

  • 子查询

  • OR 条件

  • IN 查询

  • 分片键经过函数计算

需要根据 pgdog 当前版本能力进行验证。


四、运维管理篇

4.1 启动与停止

# 前台启动(调试用) pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml run # 后台启动 nohup pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml run > /var/log/pgdog.log 2>&1 & # 查看进程 ps aux | grep pgdog # 优雅停止(等待活跃事务完成,默认 60s 超时) kill -TERM $(pidof pgdog) # 立即强制停止 kill -KILL $(pidof pgdog)

4.2 systemd 服务管理

Service 文件

# /etc/systemd/system/pgdog.service [Unit] Description=pgdog - PostgreSQL Connection Pooler & Proxy Documentation=https://github.com/pgdogdev/pgdog After=network-online.target postgresql-15.service Wants=network-online.target [Service] Type=simple User=root Group=root ExecStart=/usr/local/bin/pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml run # SIGTERM 优雅停止,给活跃事务 60s 完成 ExecStop=/bin/kill -TERM $MAINPID TimeoutStopSec=70 # 异常退出自动重启 Restart=on-failure RestartSec=5s # 资源限制 LimitNOFILE=65536 LimitNPROC=4096 # 日志接入 journald StandardOutput=journal StandardError=journal SyslogIdentifier=pgdog Environment="RUST_LOG=info" [Install] WantedBy=multi-user.target
  • 实际拷机环境中配置如下
[root@openEuler ~/lxm]# cat /etc/systemd/system/pgdog.service [Unit] Description=Pgdog PostgreSQL Pool Router After=network.target [Service] Type=simple ExecStart=/usr/local/pgdog/pgdog --config /usr/local/pgdog/pgdog.toml --users /usr/local/pgdog/users.toml Restart=on-failure RestartSec=5 LimitNOFILE=65535 [Install] WantedBy=multi-user.target

常用命令

# 注册与启动 systemctl daemon-reload systemctl enable pgdog # 开机自启 systemctl start pgdog # 运维操作 systemctl status pgdog # 查看状态 systemctl stop pgdog # 优雅停止(最多 60s) systemctl restart pgdog # 重启 # 日志 journalctl -u pgdog -f # 实时跟踪 journalctl -u pgdog -n 100 # 最近 100 行 journalctl -u pgdog --since "10 min ago"

4.3 热重载配置

pgdog 的核心运维优势:绝大多数配置变更无需重启

支持热重载的参数

类别 支持热重载 需重启
default_pool_size / min_pool_size
pooler_mode ⚠️ 涉及连接状态管理,建议低峰验证
idle_timeout / query_timeout
load_balancing_strategy
read_write_split / read_write_strategy
ban_timeout / healthcheck_*
log_level / log_format
新增/修改 [[databases]] 条目 ⚠️ 根据版本验证
新增/修改 [[users]] 条目 ⚠️ 根据版本验证
host / port
workers
tls_*

⚠️ pooler_mode 虽然支持热重载,但从 session 切换到 transaction(或反之)可能影响已有连接的状态管理,建议在低负载时操作。

热重载流程

# 1. 编辑配置 vim /etc/pgdog/pgdog.toml # 2. 语法检查(可选但推荐) pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml configcheck # 3. 热重载(不中断服务) PGPASSWORD=admin_pass psql -h 127.0.0.1 -p 6432 -U admin -d admin -c 'RELOAD;' # 4. 验证 PGPASSWORD=admin_pass psql -h 127.0.0.1 -p 6432 -U admin -d admin -c 'SHOW pools;'

4.4 Admin 数据库命令详解

连接 Admin 数据库:

PGPASSWORD=<admin_password> psql -h 127.0.0.1 -p 6432 -U admin -d admin

admin 是虚拟数据库,由 pgdog 进程内部实现,不需要在后端 PG 中预先创建。pgdog 识别到 admin 连接后直接在进程内部处理命令,不会转发给后端 PG。

查看状态命令

SHOW pools — 连接池状态(最重要)

-[ RECORD 1 ]--+------------- id | 1 database | db_biz_01 user | postgres addr | 127.0.0.1 role | primary cl_waiting | 0 # 等待获取连接的客户端数 sv_idle | 2 # 空闲后端连接数 sv_active | 3 # 活跃后端连接数 sv_idle_xact | 0 # 空闲事务中的连接数 sv_total | 5 # 后端连接总数 = sv_idle + sv_active + sv_idle_xact maxwait | 0 # 客户端最大等待时间 pool_mode | transaction paused | f # 是否暂停 banned | f # 是否被 ban healthy | t # 是否健康
字段 说明
cl_waiting 等待获取连接的客户端数(>0 说明池不够用)
sv_idle 空闲后端连接数
sv_active 活跃后端连接数
sv_idle_xact 空闲事务中的连接数
sv_total 后端连接总数 = sv_idle + sv_active + sv_idle_xact
maxwait 客户端最大等待时间
paused 是否暂停 (PAUSE 命令)
banned 是否故障封禁
healthy 健康状态

SHOW servers — 后端连接详情

pool_id | database | user | addr | state | transactions | queries ---------+-----------+----------+------------+-------+--------------+--------- 1 | db_biz_01 | postgres | 127.0.0.1 | idle | 3795 | 26481

SHOW stats — 统计信息

database | user | total_xact_count | total_query_count | total_errors | total_wait_time ------------+----------+------------------+-------------------+--------------+----------------- db_biz_01 | postgres | 123456 | 789012 | 0 | 12345

统计字段说明:

  • total_xact_count:总事务数

  • total_query_count:总查询数

  • total_errors:总错误数

  • total_received / total_sent:收发字节数

  • total_xact_time / total_query_time / total_wait_time:事务/查询/等待总耗时

SHOW config — 当前运行时完整配置

管理命令

命令 说明 使用场景
RELOAD; 热重载配置文件 修改配置后
PAUSE postgres; 暂停某数据库(新请求排队) 后端 PG 维护
RESUME postgres; 恢复暂停的数据库 维护完成
MAINTENANCE ON; 全局维护模式 整体维护
MAINTENANCE OFF; 退出维护模式 维护完成

4.5 连接数精算方法

pgdog 的最大后端连接数为:

MAX_BACKEND = Σ (per_database_pool_size × database_user_count) 下所有 (user,db) 组合

简化公式(所有库用统一 pool_size):

MAX_BACKEND = 用户数 × 数据库数 × default_pool_size PG max_connections ≥ MAX_BACKEND + 50 # 预留运维连接

示例计算

场景 计算 PG max_connections 建议
1用户 × 42库 × 10池 420 500
5用户 × 40库 × 15池 3,000 3,200
10用户 × 10库 × 20池 2,000 2,200

⚠️ 重要:实际运行中 sv_active 远小于理论最大值。pooler_mode = "transaction" 下,只有同时有活跃事务的连接才会被占用。

4.6 故障排查清单

现象 排查方法 常见原因与解决
客户端连不上 systemctl status pgdog / ss -tlnp | grep 6432 pgdog 未启动或端口被占用
认证失败 检查 auth_typeusers.toml 密码 trust 模式也需 users.toml 定义用户;password 字段必须用明文
"no pool for database" SHOW pools; pgdog.toml 中缺少该 database 的 [[databases]] 条目,或 users.toml 中未定义该用户对该库的访问权限
连接池耗尽 SHOW pools;sv_active ≈ sv_totalcl_waiting > 0 增大 default_pool_size,或增大 PG max_connections
后端数据库异常 SHOW pools;healthy=fbanned=t 检查后端 PG 状态,ban_timeout 过后自动恢复
配置未生效 SHOW config | grep <参数名> 确认已执行 RELOAD; 或重启
慢查询占连接 SHOW pools;sv_active 持续高位 设置 query_timeout,定位慢查询
性能瓶颈 SHOW stats;total_wait_time 增长 增大 workersdefault_pool_size,考虑分离部署
checkout timeout(写操作) 检查 role 配置 单主库用了 role="auto" 但未启用 LSN 检查(见 7.7)

五、性能调优篇

5.1 压测数据分析

基于 pgbench TPC-B 同机部署 (8核 aarch64) 的压测结果:

测试环境

pgbench (客户端) │ ├── 直连测试:pgbench → PostgreSQL (127.0.0.1:5433) │ └── 代理测试:pgbench → pgdog (127.0.0.1:6432) → PostgreSQL (127.0.0.1:5433)
  • pgbench 与 pgdog / PostgreSQL 部署在同一台服务器上(aarch64, 8核)

  • 使用 TPC-B 基准测试(-b tpcb),scale factor = 50,每轮 60 秒

数据汇总

客户端数 直连 TPS pgdog TPS 比率 直连延迟 代理延迟 TPS 损失
1 633 402 63.5% 1.58ms 2.49ms 36.5%
4 2,077 1,516 73.0% 1.93ms 2.64ms 27.0%
8 3,706 2,272 61.3% 2.16ms 3.52ms 38.7%
16 5,159 3,161 61.3% 3.10ms 5.06ms 38.7%
32 6,285 3,780 60.1% 5.09ms 8.47ms 39.9%
64 7,081 3,997 56.4% 9.04ms 16.01ms 43.6%

TPS 趋势图

TPS 7000 ┤ ● 7081 (直连) │ 6000 ┤ ● 6285 │ 5000 ┤ ● 5159 │ 4000 ┤ ● 3706 ▲ 3997 (代理) │ ▲ 3780 3000 ┤ ● 2077 ▲ 3161 │ ▲ 2272 2000 ┤ ▲ 1516 │ 1000 ┤ ▲ 902 ├───▲────────────────────────────────────────── 0 1 2 4 8 16 32 64 (客户端) ● 直连 PostgreSQL ▲ pgdog 代理

性能开销分析

  • 固定开销:每条请求经过协议解析 → PgQuery AST 解析 → 路由决策 → 连接池调度 → 响应回传

  • 低并发 (1~4):固定开销占比高,绝对延迟增量 ~1ms,可接受

  • 中并发 (8~32):比率稳定在 60%~61%,线性扩展

  • 高并发 (64):pgdog 同机部署时与 PG 竞争 CPU,是主要瓶颈

结论:同机部署引入约 27%~44% 的 TPS 开销,延迟增加约 37%~77%。这是全功能代理的正常开销——pgdog 与 PgBouncer 的定位不同:

PgBouncer 是轻量级 PostgreSQL 连接池,主要解决:

  • 客户端连接复用

  • backend connection 数量控制

  • session/transaction pooling

pgdog 在连接池基础上增加 SQL 感知能力,例如:

  • SQL 解析

  • 读写分离

  • 分片路由

  • 查询级流量控制

因此 pgdog 更适合需要数据库流量治理能力的场景。

5.2 配置优化逐项分析

以下基于 42 库实际生产环境(8核 / 7.5GB RAM / pgdog commit f2f2a3d)的运行状态采集,逐项分析配置优化点。

运行状态采集(某时刻快照)

指标
客户端连接数 93
后端空闲连接数 60
后端活跃连接数 0
等待连接的客户端 0(所有池均为 0)
PG 实际后端进程数 60(全部 idle)
query_cache 命中率 103 / (103+799) ≈ 11.4%
PG 错误日志 does not exist 39 条(prepared statement 相关,无害)
pgdog 内存占用 ~30MB RSS / 485MB VSZ

问题 1:default_pool_size = 10 过大(🔴 高优先级)

现状:42 个数据库 × 10 连接 = 最多 420 后端连接

问题

  • 实际只有 60 个后端连接在使用,利用率仅 14%(60/420)

  • 420 连接 × 每连接约 10MB PG 内存 ≈ 4.2GB,加上 shared_buffers 512MB,接近物理内存

  • PG max_connections = 1000,420 已占 42%

优化建议

# 方案 A:统一降低(推荐) default_pool_size = 5 # 方案 B:按库差异化配置(更精细) [[databases]] name = "foundation" host = "127.0.0.1" port = 54321 role = "primary" pool_size = 10 # 高频库保持 10 [[databases]] name = "ras" host = "127.0.0.1" port = 54321 role = "primary" pool_size = 8 # 次高频库

预期效果:方案 A 降至 42 × 5 = 210 后端连接,节省 50%。

问题 2:min_pool_size = 0 无预热(🟡 中优先级)

首次请求需新建连接,SCRAM-SHA-256 认证需 3 次网络往返,冷启动延迟约 50ms。

min_pool_size = 2 # 全局默认预热 2 个连接

注意:42 库 × 2 = 84 个预热连接,需确保 PG max_connections 足够。

问题 3:healthcheck_interval = 300000(5分钟)过长(🟡 中优先级)

PG 宕机后最长需 5 分钟才能检测到。

healthcheck_interval = 30000 # 30 秒检查一次

问题 4:idle_timeout = 600000(10分钟)偏长(🟡 中优先级)

空闲连接占用 PG 资源,配合 min_pool_size = 2 可缩短回收时间。

idle_timeout = 300000 # 空闲 5 分钟回收

问题 5:query_timeout = 300000(5分钟)过长(🟡 中优先级)

OLTP 场景下 5 分钟的查询通常是异常,慢查询长时间占用连接可能导致池耗尽。

query_timeout = 60000 # OLTP 场景建议 60 秒

问题 6:checkout_timeout = 5000(5秒)可能不够(🟢 低优先级)

高并发场景可适当增加:

checkout_timeout = 10000 # 10 秒

问题 7:log_connections = true + log_disconnections = true 日志量大(🟢 低优先级)

生产环境稳定后可关闭:

log_connections = false log_disconnections = false

问题 8:query_cache 命中率低(11.4%)(🟢 低优先级,预期行为)

prepared_statements = "extended" 模式下,pgdog 的查询缓存与 prepared statement 机制重叠。在单主库 + 无分片场景下,query_cache 价值有限,低命中率是正常的。如需节省内存可设置 query_cache_limit = 0

PG 侧优化

# postgresql.conf 优化建议 # pgdog 最大后端连接数 × 1.5(含管理连接、健康检查等余量) max_connections = 400 # 原 1000 → 400,配合 pgdog 连接池

5.3 参数调优建议

参数 默认值 推荐值 原因
workers 2 CPU核数 × 0.5~1 充分利用多核。8核 → 4~8
default_pool_size 10 5~50 多库场景建议 5,单库可 50
min_pool_size 1 2~5 消除冷启动延迟
pooler_mode transaction transaction 绝大多数场景
prepared_statements extended extended 兼容大多数 ORM/驱动
load_balancing_strategy random least_active_connections 多副本时更均匀
idle_timeout 60s 300s~600s 配合 min_pool_size,减少频繁建连/断连
connect_timeout 5s 5s 默认合理
checkout_timeout 5s 5~10s 高并发时给客户端更多等待时间
query_timeout 无限 60~300s 防止慢查询占连接
server_lifetime 24h 1~24h 防止连接老化,配合 jitter
connect_attempts 1 2~3 提高连接建连成功率
healthcheck_interval 30s 30s 故障检测及时,开销可控

独立部署 vs 同机部署

如果 TPS 是关键指标且 pgdog 不是必需的(不需要读写分离/分片),考虑:

  1. 将 pgdog 部署到独立服务器(消除 CPU 争用)

  2. 如果只需要纯连接池,可评估 PgBouncer

5.4 监控指标说明

Prometheus 端点http://<pgdog_host>:9090/metrics

关键指标:

指标 说明 告警阈值建议
pgdog_pool_waiting 等待连接的客户端数 > 10
pgdog_pool_active 活跃后端连接数 > pool_size × 0.9
pgdog_pool_banned 被封禁的池 > 0
pgdog_errors_total 总错误数 速率突增
pgdog_wait_time 等待时间 > 100ms

Admin 命令日常巡检

#!/bin/bash # 日常巡检脚本 ADMIN_PASS="your_password" echo "=== 连接池高水位 ===" PGPASSWORD=$ADMIN_PASS psql -h 127.0.0.1 -p 6432 -U admin -d admin -c "SHOW pools;" | awk -F'|' 'NR>2 && NF>1 {print}' echo "=== 被封禁的池 ===" PGPASSWORD=$ADMIN_PASS psql -h 127.0.0.1 -p 6432 -U admin -d admin -c "SHOW pools;" | grep -i "t" echo "=== 错误统计 ===" PGPASSWORD=$ADMIN_PASS psql -h 127.0.0.1 -p 6432 -U admin -d admin -c "SHOW stats;"

5.5 Prometheus 告警建议

基于 openmetrics_port = 9090,建议配置以下告警:

# pgdog 后端连接使用率 > 80% - alert: PgdogPoolNearExhaustion expr: pgdog_pool_active / pgdog_pool_size > 0.8 for: 5m # 客户端等待连接 - alert: PgdogClientWaiting expr: pgdog_pool_waiting > 0 for: 1m # 被封禁的池 - alert: PgdogPoolBanned expr: pgdog_pool_banned > 0 for: 1m

六、编译安装篇

6.1 源码编译

# 1. 获取源码 git clone https://github.com/pgdogdev/pgdog.git cd pgdog # 2. 配置 Cargo 国内镜像(可选,国内加速) mkdir -p ~/.cargo cat >> ~/.cargo/config.toml << 'EOF' [source.crates-io] replace-with = 'ustc' [source.ustc] registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/" [registries.crates-io] protocol = "sparse" [http] multiplexing = false EOF # 3. aarch64 环境需删除 x86_64 专用 linker 配置 sed -i '/[target.x86_64-unknown-linux-gnu]/,/mold"]/d' .cargo/config.toml # 4. 编译 cargo build --release # 5. 安装 cp target/release/pgdog /usr/local/bin/pgdog chmod +x /usr/local/bin/pgdog # 6. 创建配置目录 mkdir -p /etc/pgdog # 7. 验证 pgdog --version

关键配置说明

6.2 相关文件总览

编译安装完成后,pgdog 相关文件分布在以下位置:

路径 说明 大小
/usr/local/bin/pgdog 可执行文件 31 MB
/etc/pgdog/pgdog.toml 代理主配置文件
/etc/pgdog/users.toml 用户认证配置文件
/etc/systemd/system/pgdog.service systemd 服务文件
/build/pgdog/ 源码目录
/build/pgdog/target/release/pgdog 编译产物 31 MB
/root/.cargo/config.toml Cargo 全局配置(镜像源等)

6.3 常见问题

问题 原因 解决
GLIBC_2.38/2.39 not found 预编译二进制 glibc 版本过高 在目标机器上源码重新编译(切勿升级系统 glibc)
Cargo HTTPS 下载 crate 失败 HTTP/2 多路复用在部分网络不稳定 配置国内镜像 + http.multiplexing = false
trust 模式认证失败 pgdog 即使 auth_type="trust" 仍需 users.toml 定义用户 添加 [[users]] 条目,password 可为占位符
admin 密码未设置被随机生成 不设置则自动生成 _pgdog_XXXX 显式设置 [admin].password,debug 模式下日志会打印

七、踩坑实录与常见陷阱

以下问题均在 openEuler 22.03 LTS-SP4 (aarch64) 环境实战中遇到,记录根因与解决方案,避免二次踩坑。

7.1 超时参数不支持时间单位字符串

现象

ERROR failed to load pgdog.toml: invalid type: string "5s", expected u64, line 43 Error: MissingField("invalid type: string "5s", expected u64", 43)

根因:pgdog 的超时参数类型为 u64(毫秒整数),不支持 "5s" / "10m" / "1h" 等字符串格式。这与 PgBouncer 的配置习惯不同,容易踩坑。

修正方法

参数 错误写法 正确写法 换算
connect_timeout "5s" 5000 5秒 × 1000
checkout_timeout "5s" 5000 5秒 × 1000
idle_timeout "10m" 600000 10分钟 × 60 × 1000
client_idle_timeout "30m" 1800000 30分钟 × 60 × 1000
query_timeout "300s" 300000 300秒 × 1000
healthcheck_interval "30s" 30000 30秒 × 1000
healthcheck_timeout "5s" 5000 5秒 × 1000
ban_timeout "1m" 60000 1分钟 × 60 × 1000

建议:注释中标注原始值(如 # 原 5s),方便后续维护理解含义。

7.2 psql 默认走 Unix socket 导致连不上 pgdog

现象

psql -U postgres -d postgres -p 6432 psql: error: connection to server on socket "/tmp/.s.PGSQL.6432" failed: No such file or directory

根因:pgdog 监听的是 TCP 端口 0.0.0.0:6432,不创建 Unix socket 文件。而 psql 不指定 -h 时默认使用 Unix socket 连接。

解决:必须加 -h 参数强制走 TCP:

# 正确 psql -U postgres -d postgres -h 127.0.0.1 -p 6432 # 或 psql -U postgres -d postgres -h localhost -p 6432 # 错误(走 Unix socket,连不上) psql -U postgres -d postgres -p 6432

7.3 users.toml 的 password 字段必须用明文密码

现象

FATAL: password for user "postgres" and database "postgres" is wrong, or the database does not exist

根因:从 pg_authid.rolpassword 复制 SCRAM-SHA-256 哈希值放入 users.tomlpassword 字段,pgdog 无法正确使用。虽然哈希值与 PG 中一致,但 pgdog 的 password 字段需要明文密码来参与 SCRAM 握手(客户端→pgdog 和 pgdog→后端PG 两段认证都需要明文)。

正确做法

# users.toml — 正确:用明文密码 [[users]] name = "postgres" all_databases = true password = "Admin123@"
# users.toml — 错误:不能用 pg_authid 中的 SCRAM 哈希 [[users]] name = "postgres" all_databases = true password = "SCRAM-SHA-256$4096:JquNCpGbfTQJJbVVa+ARwQ==$0CVeobl1n9jqcmo3MiamoTCHsnuPxwQAl3LlrdEeMwY=:LK8dMwZwMlwM28Fmqror/eQuXqs0rcuxh38DNmRITLw="

7.4 [admin] 段的 password 也必须用明文

现象

psql -U admin -d admin -p 6432 -h 127.0.0.1 FATAL: password for user "admin" and database "admin" is wrong, or the database does not exist

根因:与 7.3 相同,[admin].password 也不支持 SCRAM 哈希格式,必须用明文。

正确做法

[admin] name = "admin" user = "admin" password = "Admin_147@" # 明文密码,不能用 SCRAM 哈希

7.5 SHOW POOLS 等管理命令必须在 admin 库执行

现象

postgres=# SHOW pools; FATAL: checkout timeout server closed the connection unexpectedly

根因SHOW pools / SHOW servers / RELOAD 等是 pgdog Admin 管理数据库的命令,不是标准 SQL。在业务库(如 postgres)中执行时,pgdog 会把它当成普通 SQL 转发给后端 PG,后端 PG 不认识这个命令,同时 transaction 模式下的连接复用机制会导致 checkout timeout。

正确做法

# 连接 admin 虚拟数据库执行管理命令 PGPASSWORD=Admin_147@ psql -U admin -d admin -p 6432 -h 127.0.0.1 -c "SHOW pools;" # 交互式 PGPASSWORD=Admin_147@ psql -U admin -d admin -p 6432 -h 127.0.0.1 admin=# SHOW pools; admin=# SHOW servers; admin=# SHOW stats; admin=# RELOAD;

7.6 pgdog.toml 与 users.toml 中的 admin 用户是两套体系

结论不需要两处都配 admin 用户

配置位置 认证方向 用途
pgdog.toml [admin] 客户端 → pgdog(管理库) 连接 admin 虚拟数据库,执行管理命令
users.toml [[users]] 客户端 → pgdog(业务库) 连接业务数据库,执行普通 SQL

典型配置

# pgdog.toml — 管理库认证 [admin] name = "admin" user = "admin" password = "Admin_147@"
# users.toml — 业务库认证 [[users]] name = "postgres" all_databases = true password = "Admin123@"

特殊情况:只有当需要用 admin 用户通过 pgdog 连接后端 PG 的业务库时,才需要在 users.toml 中也加一条 admin 的 [[users]]。但这和 [admin] 段是两回事。

7.7 role=“auto” + LSN 检查禁用导致 checkout timeout(最关键陷阱)

现象

Go 程序(bdatabase + pgx)通过 pgdog 连接 PG,SELECT 正常,INSERT/UPDATE/DELETE/CREATE TABLE 报 checkout timeout。psql 命令行一切正常。

FATAL: checkout timeout (SQLSTATE 58000)

根因

[[databases]]role = "auto" 配合 lsn_check_delay = MAX(默认禁用),导致 pgdog 的自动角色检测被禁用。此时 pgdog 无法确定后端是 primary 还是 replica,连接池行为异常——写操作无法获取连接。

pgdog 启动时会输出 WARN 日志(容易被忽略):

WARN database "xxx" has a role set to "auto" but LSN checks are disabled: this disables automatic role detection

实验矩阵

配置 结果 说明
1库 + transaction + extended + role=primary ✅ 8/8 基准通过
2库 + transaction + extended + role=primary ✅ 8/8 多库无影响
10库 + transaction + extended + role=primary ✅ 8/8 多库无影响
42库 + transaction + extended + role=primary ✅ 8/8 多库无影响
42库 + transaction + extended + role=auto ❌ 4/8 checkout timeout
42库 + session + extended + role=auto ✅ 8/8 session 模式绕过了问题

关键结论

  • **role = "auto"** 不是"自动检测"的万能配置,它需要 LSN 检查机制配合才能工作

  • 单主库无副本场景,必须用 **role = "primary"**,不能用 role = "auto"

  • 只有在配合 Patroni 等高可用方案、且正确配置了 lsn_check_delay = 0 时,才应使用 role = "auto"

解决

# 单主库场景 — 必须用 primary [[databases]] name = "test" host = "127.0.0.1" port = 54321 role = "primary" # ✅ 正确 # 错误:auto 需要配合 LSN 检查 # role = "auto" # ⚠️ 需要配合角色检测配置

如果确实需要 auto 角色,必须同时配置 LSN 检查:

[general] lsn_check_delay = 0 # 0 = 启用自动角色检测 lsn_check_interval = 5000 # 5s 检查一次 lsn_check_timeout = 5000 # 5s 超时

7.8 transaction 模式下 PG 日志出现 prepared statement does not exist 错误

现象

Go 程序(bdatabase + pgx)通过 pgdog transaction 模式连接 PG,业务功能全部正常(8/8 通过),但 PG 错误日志中出现大量:

ERROR: prepared statement "pgx_0" does not exist STATEMENT: deallocate "pgx_0" ERROR: prepared statement "pgx_1" does not exist STATEMENT: deallocate "pgx_1"

验证数据(42 库 + transaction + extended + role=primary):

  • Go 测试结果:8/8 PASS,全部功能正常

  • PG 错误日志统计:13 条 does not exist 错误,涉及 pgx_0 ~ pgx_4

  • 无其他类型错误

根因

这是 transaction 模式 + prepared statements 的已知行为,属于无害错误:

  1. 事务结束时,pgdog 对后端连接执行 DISCARD ALL,该命令会 隐式清除所有 prepared statements(等效于 DEALLOCATE ALL

  2. 连接归还到池后,pgx 驱动在下次使用该连接时,发现本地缓存的 statement 名已不存在于服务端,会尝试 DEALLOCATE "pgx_N" 来清理本地缓存

  3. 但服务端的 statement 已被 DISCARD ALL 清除,因此 DEALLOCATE 报错 does not exist

  4. pgx 驱动会 忽略此错误,重新 PREPARE 该 statement,后续操作正常

时序图

Client (pgx) pgdog PostgreSQL | | | |--- COMMIT ------->| | | |--- DISCARD ALL -->| ← 清除所有 prepared statements | |<-- ReadyForQuery -| | [连接归还到池] | | | | | |--- 下次请求 ------>| | | |--- DEALLOCATE ----> ← pgx 尝试清理旧 statement | |<-- ERROR: does not exist ← 无害错误 | |--- PREPARE ------->| ← pgx 重新创建 statement | |<-- ParseComplete --| | |--- BIND/EXECUTE -->| ← 正常执行

影响评估

维度 评估
业务功能 ✅ 无影响,所有操作正常完成
数据一致性 ✅ 无影响
性能 ⚠️ 轻微影响:每次连接复用后首次使用 statement 需重新 PREPARE,增加一次网络往返
日志噪音 ⚠️ PG 错误日志会产生噪音,可能干扰问题排查
连接安全 ✅ 无影响,pgdog 的 DISCARD ALL 是正确行为

缓解措施(可选):

  1. 降低日志噪音:在 postgresql.conf 中配置 log_min_messages = WARNING(默认已是此值,ERROR 级别日志仍会记录)

  2. pgx 驱动侧:设置 statement_cache_capacity = 0 可完全禁用 prepared statement 缓存(但会失去 prepared statement 的性能优势)

  3. 接受现状:这是 transaction 模式下 prepared statement 的固有行为,pgdog 和 pgx 都在正确处理

关键结论:此错误 不需要修复,只有当此错误伴随业务功能异常时,才需要进一步排查。

7.9 pgdog hash 生成的密码与 pg_authid 中不同

现象

# PG 中创建用户 postgres=# create user admin with password 'Admin_147@'; postgres=# select rolpassword from pg_authid where rolname='admin'; # 输出: SCRAM-SHA-256$4096:NvGQj6TWWFos1SzGhyopvw==$gWRHZK1JSDh0zNMZ98K9aj0CE2DaE607Ee53pkDbhAU=:YRYwEGDGzkZVKCM2MVVscjUrYjkfEySiGLLqPFTCi70= # pgdog hash 生成 pgdog hash Admin_147@ # 输出: SCRAM-SHA-256$4096:Nabo9Vs8BYgg6iEIZl/J6w==$ANIj3dAKyQb7NZ5SVPs5dxpH5Rq8XmwFJ/BEq9PsSJQ=:lmsaLR2APCeGt6eKC3panMmP7LiBYO1FbcN26Zfz9zo=

两者不同,但都能验证同一个明文密码

根因:SCRAM-SHA-256 的密码哈希包含一个随机 salt,每次计算时 salt 不同,最终哈希值就不同。

SCRAM-SHA-256$4096:Nabo9Vs8BYgg6iEIZl/J6w==$ANIj3dAKyQb7NZ5SVPs5dxpH5Rq8XmwFJ/BEq9PsSJQ=:... ↑迭代次数 ↑salt(随机) ↑StoredKey(由salt+密码派生)
  • PostgreSQL create user 时:生成随机 salt A → 计算出哈希值 X

  • pgdog hash 时:生成随机 salt B → 计算出哈希值 Y

salt 不同 → 哈希值不同,但都能用来验证同一个密码

pgdog hash 的用途:生成 SCRAM-SHA-256 哈希供 pgdog 配置文件使用,但当前版本 **password** 字段只支持明文,不支持哈希格式。

7.10 踩坑速查表

现象 根因 解决
invalid type: string "5s", expected u64 超时参数不支持时间单位字符串 改为毫秒整数,如 5000
No such file or directory (socket) psql 默认走 Unix socket -h 127.0.0.1
password ... is wrong (业务库) users.toml 用了 SCRAM 哈希 改为明文密码
password ... is wrong (admin 库) [admin].password 用了 SCRAM 哈希 改为明文密码
checkout timeout (show pools) 管理命令在业务库执行 连接 admin 库执行
admin 库连不上 不了解 admin 是虚拟库 不需要创建,直接连
**checkout timeout**(写操作) role=“auto” + LSN 检查禁用 单主库用 role="primary"
PG 日志 prepared statement "pgx_N" does not exist transaction 模式下 DISCARD ALL + pgx 驱动的已知行为 无害错误,不影响业务(见 7.8)

八、命令行工具

8.1 子命令概览

pgdog [OPTIONS] [COMMAND]
命令 说明
run 启动 pgdog 代理服务
hash 生成 SCRAM-SHA-256 密码哈希(注意:当前 users.toml 的 password 字段只支持明文)
route 测试查询路由(离线分析 SQL 会被路由到哪个分片/节点)
configcheck 检查配置文件语法
data-sync 使用逻辑复制同步数据
schema-sync 同步数据库 schema
setup 为分片集群执行必要的配置步骤
help 帮助信息

8.2 常用命令示例

# 1. 生成密码哈希 pgdog hash "mypassword" # 2. 检查配置 pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml configcheck # 3. 测试查询路由 echo "SELECT * FROM users WHERE id = 1;" > /tmp/test.sql pgdog --config /etc/pgdog/pgdog.toml --users /etc/pgdog/users.toml route --user postgres --database postgres --file /tmp/test.sql # 4. 分片集群初始化 pgdog --config /etc/pgdog/pgdog.toml setup --database sharded_db

8.3 客户端连接方式

# psql 连接 PGPASSWORD=postgres psql -h 127.0.0.1 -p 6432 -U postgres -d postgres # 应用程序连接串 # postgresql://postgres:postgres@127.0.0.1:6432/postgres # pgbench 压测 pgbench -h 127.0.0.1 -p 6432 -U postgres -d postgres -T 60 -c 10 -j 10

注意:客户端连接 pgdog 与直连 PostgreSQL 的唯一区别是端口号(6432 vs 5432/5433),其他参数完全相同。务必加 -h 参数强制走 TCP。


附录

A. 配置文件模板位置

文件 路径 说明
可执行文件 /usr/local/bin/pgdog pgdog 二进制
主配置 /etc/pgdog/pgdog.toml 代理配置
用户配置 /etc/pgdog/users.toml 用户认证
systemd /etc/systemd/system/pgdog.service 服务管理
源码 /build/pgdog/ 源码目录
编译产物 /build/pgdog/target/release/pgdog release 编译产物
Cargo 配置 /root/.cargo/config.toml Rust 镜像源等

B. 环境变量覆盖

pgdog 支持通过环境变量覆盖部分配置项。

具体支持范围取决于当前版本实现。

生产环境建议:

  • 稳定配置使用 TOML 文件管理

  • 容器环境使用环境变量覆盖部署相关参数

  • 发布前验证目标版本支持的配置映射

export PGDOG_HOST=0.0.0.0 export PGDOG_PORT=6432 export PGDOG_WORKERS=4 export PGDOG_DEFAULT_POOL_SIZE=10 export PGDOG_MIN_POOL_SIZE=2 export PGDOG_POOLER_MODE=transaction export PGDOG_AUTH_TYPE=scram export PGDOG_CONNECT_TIMEOUT=5000 export PGDOG_HEALTHCHECK_INTERVAL=30000 export PGDOG_LOG_LEVEL=debug export PGDOG_LOG_FORMAT=json export PGDOG_OPENMETRICS_PORT=9090 export PGDOG_ADMIN_PASSWORD=my_admin_pass

注意:并非所有配置项都支持环境变量覆盖,复杂类型(枚举、Option、Vec 等)可能不支持。

C. 快速运维命令速查

操作 命令
启动 systemctl start pgdog
停止 systemctl stop pgdog
重启 systemctl restart pgdog
开机自启 systemctl enable pgdog
查看状态 systemctl status pgdog
实时日志 journalctl -u pgdog -f
语法检查 pgdog --config ... --users ... configcheck
热重载 psql -h ... -p 6432 -U admin -d admin -c 'RELOAD;'
查看连接池 同上 -c 'SHOW pools;'
查看服务器 同上 -c 'SHOW servers;'
查看统计 同上 -c 'SHOW stats;'
暂停某库 同上 -c 'PAUSE db_name;'
维护模式 同上 -c 'MAINTENANCE ON;'
生成密码哈希 pgdog hash "password"
测试路由 pgdog route --user postgres --database postgres --file /tmp/test.sql

D. 一键压测脚本

#!/bin/bash # pgbench 对比压测脚本 RESULT_FILE="/tmp/bench_results.txt" CLIENTS="1 4 8 16 32 64" T_DURATION=60 echo "====== 直连压测 ======" | tee $RESULT_FILE for c in $CLIENTS; do echo "--- 客户端: $c (T=${T_DURATION}s) ---" | tee -a $RESULT_FILE pgbench -h 127.0.0.1 -p 5433 -U postgres -d postgres -T $T_DURATION -c $c -j $c 2>&1 | tee -a $RESULT_FILE sleep 2 done echo "" | tee -a $RESULT_FILE echo "====== pgdog 代理压测 ======" | tee -a $RESULT_FILE for c in $CLIENTS; do echo "--- 客户端: $c (T=${T_DURATION}s) ---" | tee -a $RESULT_FILE pgbench -h 127.0.0.1 -p 6432 -U postgres -d postgres -T $T_DURATION -c $c -j $c 2>&1 | tee -a $RESULT_FILE sleep 2 done echo "压测完成,结果保存在 $RESULT_FILE"

E. 优化后推荐配置

pgdog.toml 优化版(以下配置不一定符合实际场景, 还是需根据实际情况酌情修改适配)

[general] host = "0.0.0.0" port = 6432 # ===== 连接池核心参数(已优化) ===== default_pool_size = 5 # 原 10 → 5,降低后端连接总数 min_pool_size = 0 # 原 0 → 2,预热减少冷启动延迟 pooler_mode = "transaction" # ===== 认证 ===== auth_type = "scram" # ===== 超时控制(已优化) ===== connect_timeout = 5000 checkout_timeout = 10000 # 原 5000 → 10000,高并发时更从容 idle_timeout = 300000 # 原 600000 → 300000,空闲 5 分钟回收 client_idle_timeout = 1800000 query_timeout = 60000 # 原 300000 → 60000,OLTP 场景 60 秒足够 # ===== 性能调优 ===== workers = 6 load_balancing_strategy = "least_active_connections" prepared_statements = "extended" # ===== 健康检查(已优化) ===== healthcheck_interval = 30000 # 原 300000 → 30000,30 秒检查一次 healthcheck_timeout = 5000 ban_timeout = 60000 # ===== 日志与监控(已优化) ===== log_level = "info" log_format = "json" log_connections = false # 原 true → false,减少日志量 log_disconnections = false # 原 true → false,减少日志量 openmetrics_port = 9090 [admin] name = "admin" user = "admin" password = "Admin_147@" # ===== 高频库单独配置 ===== [[databases]] name = "foundation" host = "127.0.0.1" port = 54321 role = "primary" pool_size = 10 # foundation 保持 10 连接 min_pool_size = 3 # 预热 3 个 [[databases]] name = "ras" host = "127.0.0.1" port = 54321 role = "primary" pool_size = 8 min_pool_size = 2 # ===== 系统默认库 ===== [[databases]] name = "postgres" host = "127.0.0.1" port = 54321 role = "primary" [[databases]] name = "template1" host = "127.0.0.1" port = 54321 role = "primary" # ===== 其余业务库使用 default_pool_size=5 ===== # (配置同现有,省略)

PostgreSQL 侧建议

# postgresql.conf 优化建议 max_connections = 400 # 原 1000 → 400,配合 pgdog 连接池 shared_buffers = 512MB # 保持不变 work_mem = 4MB # 保持不变 effective_cache_size = 2GB # 保持不变

优化效果预估

指标 优化前 优化后(预估) 改善
最大后端连接数 420 ~200(含高频库 10+8) -52%
PG 内存占用(连接) ~600MB(60×10MB) ~200MB(20×10MB,idle 回收后) -67%
冷启动首次请求延迟 ~50ms(新建连接+SCRAM) ~5ms(预热连接就绪) -90%
故障检测时间 最长 5 分钟 最长 30 秒 -90%
慢查询占坑时间 最长 5 分钟 最长 60 秒 -80%
日志 I/O 高(连接/断开全记录) -80%

变更风险评估

变更项 风险等级 说明 回滚方案
default_pool_size 10→5 🟡 中 高并发时可能 checkout_timeout 改回 10
min_pool_size 0→2 🟢 低 增加预热连接,内存略增 改回 0
healthcheck_interval 5m→30s 🟢 低 增加健康检查频率,PG 负载微增 改回 300000
idle_timeout 10m→5m 🟢 低 空闲连接更快回收 改回 600000
query_timeout 5m→60s 🟡 中 如有长查询会被杀 改回 300000
checkout_timeout 5s→10s 🟢 低 等待更久,减少超时错误 改回 5000
log_connections/disconnections 🟢 低 不影响功能 改回 true
PG max_connections 1000→400 🟡 中 需重启 PG 改回 1000

实施步骤

  1. 先调 pgdog 配置(无需重启 PG,reload pgdog 即可)

    # 备份当前配置 cp /etc/pgdog/pgdog.toml /etc/pgdog/pgdog.toml.bak # 修改配置后 reload PGPASSWORD=Admin_147@ psql -h 127.0.0.1 -p 6432 -U admin -d admin -c 'RELOAD;' # 或重启 pgdog systemctl restart pgdog
  2. 观察 1~2 小时,通过 Prometheus 监控关注:

    • sv_active:活跃连接数是否正常

    • cl_waiting:是否有客户端等待连接

    • checkout_timeout 错误是否增加

  3. 确认稳定后,再调整 PG max_connections(需重启 PG,需维护窗口)

最后修改时间:2026-08-14 15:56:27
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论