📋 文章信息
• 适用版本:PostgreSQL 15.4 / PgBouncer 1.21.0
• 部署架构:PgBouncer + repmgr 高可用集群
• 测试环境:4 核 8G / CentOS 7.9
• 本文内容:安装部署 + 三种模式 + 生产配置 + 参数调优

为什么需要连接池

PostgreSQL 每个连接都是一个独立进程,占用约 5-10MB 内存。1000 个连接就是 5-10GB 内存,这还没算实际查询消耗。

典型症状:

  • 连接数接近 max_connections 上限
  • 新连接建立缓慢(>100ms)
  • 内存占用高但 CPU 利用率低
  • 应用报 "too many clients already" 错误

什么时候该上 PgBouncer:

  1. 应用连接数 > 200
  2. 短连接频繁(HTTP 请求式查询)
  3. 需要连接数隔离(不同业务不同池)
  4. 高可用切换时需要快速重连

🚀 快速安装(CentOS 7.9)

方式 1:YUM 安装(推荐生产环境)

# 1. 安装 PostgreSQL YUM 仓库(如果已安装可跳过)
sudo yum install -y https://download.postgresql.org/pub/repos/yum/15/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 2. 安装 PgBouncer
sudo yum install -y pgbouncer15

# 3. 验证安装
pgbouncer --version
# 输出:pgbouncer 1.21.0

# 4. 配置文件位置
# /etc/pgbouncer/pgbouncer.ini    ← 主配置文件
# /etc/pgbouncer/userlist.txt     ← 用户认证文件
# /var/log/pgbouncer/             ← 日志目录

💡 系统版本提示:
CentOS 7 已于 2024 年 6 月 30 日结束官方支持(EOL)。

生产环境建议:
• 新部署:优先选择 Rocky Linux 9 / AlmaLinux 9 / Ubuntu 22.04 LTS
• 存量系统:规划迁移计划,本文配置在主流 Linux 发行版通用

方式 2:源码编译(需要定制功能时)

# 1. 安装依赖
sudo yum install -y libevent-devel openssl-devel

# 2. 下载源码
wget https://www.pgbouncer.org/downloads/files/1.21.0/pgbouncer-1.21.0.tar.gz
tar xzf pgbouncer-1.21.0.tar.gz
cd pgbouncer-1.21.0

# 3. 编译安装
./configure --prefix=/usr/local/pgbouncer --with-libevent
make
sudo make install

# 4. 初始化配置
sudo mkdir -p /etc/pgbouncer /var/log/pgbouncer
sudo cp /usr/local/pgbouncer/share/pgbouncer.ini /etc/pgbouncer/
sudo cp /usr/local/pgbouncer/share/userlist.txt /etc/pgbouncer/

方式 3:Docker 部署(测试环境)

# 方案 A:官方轻量镜像(推荐,配置灵活)
docker run -d \
  --name pgbouncer \
  -p 6432:6432 \
  -v /etc/pgbouncer:/etc/pgbouncer \
  edoburu/pgbouncer:1.21.0

# 方案 B:Bitnami 镜像(配置路径不同)
docker run -d \
  --name pgbouncer \
  -p 6432:6432 \
  -v /etc/pgbouncer/pgbouncer.ini:/opt/bitnami/pgbouncer/conf/pgbouncer.ini \
  -v /etc/pgbouncer/userlist.txt:/opt/bitnami/pgbouncer/conf/userlist.txt \
  -e POSTGRESQL_HOST=192.168.1.200 \
  -e POSTGRESQL_PORT=5432 \
  -e POSTGRESQL_USERNAME=app_user \
  -e POSTGRESQL_PASSWORD=your_password \
  bitnami/pgbouncer:1.21.0

三种连接模式

PgBouncer 有三种连接复用模式,选错会导致数据错误

1. Session 模式(session)✅

✅ 最安全,兼容性最好。

一个客户端连接在整个会话期间独占一个服务端连接。适合:

  • 使用临时表的业务
  • 使用 PREPARE 预编译语句
  • 使用 LISTEN/NOTIFY 监听
  • 需要 SET 修改会话参数
; /etc/pgbouncer/pgbouncer.ini
pool_mode = session

缺点: 连接复用率低。如果客户端保持长连接但不查询,服务端连接一直被占用。

2. Transaction 模式(transaction)⭐

⭐ 生产环境最常用。

一个客户端连接只在事务期间占用服务端连接,事务结束立即归还。适合:

  • 大多数 Web 应用
  • 短查询 + 自动提交
  • 高并发场景
; /etc/pgbouncer/pgbouncer.ini
pool_mode = transaction

⚠️ 注意: 不能在事务外使用临时表、预编译语句、SET 会话变量。跨事务的临时表会消失。

3. Statement 模式(statement)⚠️

⚠️ 最激进,最少用。

每条 SQL 语句执行完立即归还连接,连事务都不保持。适合:

  • 只读查询
  • 无事务的简单 SQL
; /etc/pgbouncer/pgbouncer.ini
pool_mode = statement

⚠️ 警告: 显式事务(BEGIN/COMMIT)会报错,因为 COMMIT 时连接已经不在你手里了。

💡 PgBouncer vs PgPool-II

很多读者混淆这两个工具,这里明确区分:

特性 PgBouncer PgPool-II
定位 轻量级连接池 数据库代理(含连接池)
连接池 ✅ 支持三种模式 ✅ 支持
读写分离 ❌ 不支持 ✅ 支持
负载均衡 ❌ 不支持 ✅ 支持
复制管理 ❌ 不支持 ✅ 支持
资源占用 极低(50MB/1000 连接) 较高
配置复杂度 简单 复杂
适用场景 纯连接池需求 需要读写分离/负载均衡

本文只讨论 PgBouncer。如果你需要读写分离,考虑 PgPool-II 或应用层路由。

生产环境配置

单实例部署

; /etc/pgbouncer/pgbouncer.ini

[databases]
; 数据库名 = 连接字符串
mydb = host=192.168.1.10 port=5432 dbname=mydb
postgres = host=192.168.1.10 port=5432 dbname=postgres

; 通配符:所有数据库共用配置
* = host=192.168.1.10 port=5432

[pgbouncer]
; ========== 基础配置 ==========
listen_addr = 0.0.0.0
listen_port = 6432
unix_socket_dir = /tmp

; ========== 认证配置 ==========
; ⚠️ PostgreSQL 15+ 默认使用 scram-sha-256,PgBouncer 1.21+ 支持
; 如果后端是 SCRAM 密码,用 scram-sha-256;如果是 MD5,用 md5
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt

; ========== 连接模式 ==========
pool_mode = transaction

; ========== 连接池大小(关键参数)==========
default_pool_size = 20
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3

; ========== 连接限制 ==========
max_client_conn = 1000
max_db_connections = 50
max_user_connections = 100

; ========== 超时设置 ==========
server_connect_timeout = 15
server_login_retry = 15
query_timeout = 300
query_wait_timeout = 120
client_idle_timeout = 0
server_idle_timeout = 600

; ========== 会话清理(安全优先)==========
; 每次连接归还都执行 DISCARD ALL,确保会话状态隔离
; 性能敏感场景可设为 0,但需应用层确保会话状态清理
server_reset_query = DISCARD ALL
server_reset_query_always = 1

; ========== 日志配置 ==========
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1
stats_period = 60

用户配置文件

# /etc/pgbouncer/userlist.txt
# 格式:"用户名" "加密后的密码"

# ✅ 推荐方式:直接复用 PostgreSQL 已加密的密码(最可靠)

# 1. 在 PostgreSQL 中创建/修改用户(自动使用 SCRAM 加密)
CREATE ROLE app_user WITH LOGIN PASSWORD 'your_password';

# 2. 查询已加密的密码(需 superuser 权限)
SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app_user';
-- 输出:app_user | SCRAM-SHA-256$4096:xxxx$yyyy:zzzz

# 3. 将 rolpassword 值直接复制到 /etc/pgbouncer/userlist.txt
"app_user" "SCRAM-SHA-256$4096:xxxx$yyyy:zzzz"

# 💡 注意:
# - pg_authid 视图需 superuser 权限访问
# - 如权限受限,可让 DBA 协助导出密码
# - 切勿手动构造 SCRAM 字符串,极易出错

# 设置严格权限,防止密码泄露
sudo chown pgbouncer:pgbouncer /etc/pgbouncer/userlist.txt
sudo chmod 600 /etc/pgbouncer/userlist.txt

# 验证权限
ls -l /etc/pgbouncer/userlist.txt
# 预期输出:-rw------- 1 pgbouncer pgbouncer ... userlist.txt

# 如果后端使用 MD5(不推荐,仅兼容旧系统):
# pg_md5 app_user your_password
# 输出:md5c4ca4238a0b923820dcc509a6f75849b
"app_user" "md5c4ca4238a0b923820dcc509a6f75849b"

启动与验证

# 1. 启动 PgBouncer(CentOS 7)
sudo systemctl start pgbouncer
sudo systemctl enable pgbouncer

# 2. 查看状态
sudo systemctl status pgbouncer

# 3. 测试连接
psql -h 127.0.0.1 -p 6432 -U app_user -d mydb

# 4. 查看日志
tail -f /var/log/pgbouncer/pgbouncer.log

🔧 管理接口使用

PgBouncer 内置管理数据库,可以查看状态和修改配置。

连接管理数据库

# 方式 1:通过 Unix Socket 连接(默认)
psql -h /tmp -p 6432 -U pgbouncer pgbouncer

# 方式 2:通过 TCP 连接(需在 pgbouncer.ini 启用)
; /etc/pgbouncer/pgbouncer.ini
[pgbouncer]
admin_users = pgbouncer_admin
stats_users = pgbouncer_monitor,postgres

# 然后连接
psql -h 127.0.0.1 -p 6432 -U pgbouncer_admin pgbouncer

常用管理命令

-- 查看所有可用命令
SHOW HELP;

-- 查看当前配置
SHOW CONFIG;

-- 查看连接池状态(核心命令)
SHOW POOLS;
-- 输出字段说明:
-- database     : 数据库名
-- user         : 池所属用户
-- cl_active    : 活跃客户端连接(正在查询)
-- cl_waiting   : 等待连接的客户端(池满时排队)⚠️ >0 表示连接池不足
-- sv_active    : 活跃服务端连接(正在执行查询)
-- sv_idle      : 空闲服务端连接(可复用)
-- sv_used      : 曾被使用过的服务端连接(统计用)
-- sv_tested    : 正在健康检查的连接
-- sv_login     : 正在登录后端的连接
-- maxwait      : 客户端最长等待时间(秒),>0 表示连接池不足

-- 🚨 告警判断:
-- cl_waiting > 0 且 maxwait 持续增长 → 需要扩容 pool_size
-- sv_idle 持续为 0 → 连接池利用率饱和

-- 查看客户端连接
SHOW CLIENTS;

-- 查看服务端连接
SHOW SERVERS;

-- 查看统计信息
SHOW STATS;

-- 查看版本
SHOW VERSION;

-- 重载配置(不中断现有连接)
RELOAD;

-- 优雅关闭
SHUTDOWN;

关键参数调优

default_pool_size

每个数据库 + 用户组合的服务端连接数上限。

推荐调优方法(按优先级):

  1. 基准压测:从 pool_size=10 开始,逐步增加,观察 TPS/延迟变化
  2. 监控指标:当 SHOW POOLS 中 cl_waiting 持续 > 0 时,考虑扩容
  3. 经验参考(仅作起点):
    • OLTP 短查询:pool_size = CPU 核数 × 3~5
    • 混合负载:pool_size = CPU 核数 × 2~3
    • 复杂查询:pool_size = CPU 核数 × 1~2(避免过多并发消耗内存)

核心原则:服务端连接数 ≈ 能充分利用 CPU 但不触发内存交换的阈值。

测试方法:

-- 查看当前连接池使用情况
SHOW STATS;

-- 查看各池的连接数
SELECT * FROM pg_stat_activity;
min_pool_size
最小空闲连接数。设为 5-10,避免冷启动时创建连接慢。

reserve_pool_size
备用连接池大小。当主池耗尽时临时启用,适合应对突发流量。

max_client_conn
客户端连接上限。PgBouncer 自身占用很少内存,可以设高(1000-5000)。

server_idle_timeout
服务端连接空闲超时(秒)。600 秒(10 分钟)比较合适,太短会导致频繁重建连接。

高可用部署(配合 repmgr)

架构说明

repmgr 集群 + PgBouncer 的标准架构:

应用层
   ↓
[PgBouncer VIP: 192.168.1.100:6432]
   ↓
[数据库集群 VIP: 192.168.1.200:5432]  ← repmgr + Keepalived 管理
   ↓
┌────┴────────┬────────┐
主库          备 1       备 2
192.168.1.10  192.168.1.11  192.168.1.12

关键区分:

  • 192.168.1.100 → PgBouncer 的 VIP(应用连接这个)
  • 192.168.1.200 → 数据库集群的 VIP(PgBouncer 连接这个)

PgBouncer 配置

[databases]
; 指向数据库集群 VIP(推荐)
; 192.168.1.200 是 repmgr + Keepalived 管理的数据库 VIP
mydb = host=192.168.1.200 port=5432 dbname=mydb

[pgbouncer]
; 启用服务器健康检查
server_check_query = SELECT 1
server_check_delay = 10

; 连接复用安全清理
server_reset_query = DISCARD ALL
server_reset_query_always = 1

; 重连控制(PgBouncer 无 max_retries 参数)
; server_connect_timeout: 连接后端超时
; server_login_retry: 登录失败重试间隔
server_connect_timeout = 10
server_login_retry = 5

配合 Keepalived 实现 VIP 漂移

# /etc/keepalived/keepalived.conf
vrrp_script chk_database {
    script "/usr/local/bin/check_database.sh"
    interval 2
    weight -20
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    
    virtual_ipaddress {
        192.168.1.200/24  # 数据库 VIP
    }
    
    track_script {
        chk_database
    }
}

repmgr 通知脚本(高级用法)

# /usr/local/bin/repmgr_pgbouncer_notify.sh
#!/bin/bash
# repmgr 切换成功后,通知 PgBouncer 重载配置

if [ "$1" = "standby_promoted" ]; then
    echo "Standby promoted, reloading PgBouncer config..."
    psql -h 127.0.0.1 -p 6432 -U pgbouncer pgbouncer -c "RELOAD;"
fi

在 repmgr.conf 中配置:

# /etc/repmgr/15/repmgr.conf
notify_standby_promote = '/usr/local/bin/repmgr_pgbouncer_notify.sh %n %u'

应用层连接配置示例

Python (psycopg2 + 连接池)

from psycopg2 import pool

# 应用层连接池(指向 PgBouncer)
connection_pool = pool.SimpleConnectionPool(
    1, 20,  # minconn, maxconn(指应用→PgBouncer)
    host="192.168.1.100",  # PgBouncer VIP
    port=6432,
    database="mydb",
    user="app_user",
    password="your_password"
)

# 获取连接
conn = connection_pool.getconn()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = %s", (1,))

# 归还连接
connection_pool.putconn(conn)

Java (HikariCP + Spring Boot)

# application.yml
spring:
  datasource:
    hikari:
      jdbc-url: jdbc:postgresql://192.168.1.100:6432/mydb
      username: app_user
      password: your_password
      driver-class-name: org.postgresql.Driver
      
      # 应用层连接池配置
      maximum-pool-size: 50    # 应用层池大小
      minimum-idle: 10
      connection-timeout: 30000
      idle-timeout: 600000

💡 两级连接池注意:应用层连接池(如 HikariCP)+ PgBouncer = 双重复用。
 

协调公式:
假设:
• 应用实例数:4 个
• PgBouncer max_client_conn: 1000
• 每个应用实例的 HikariCP maximum-pool-size: ?

推荐计算:
application_pool_size = (max_client_conn * 0.8) / app_instances
= (1000 * 0.8) / 4 = 200

但需考虑:
• 预留 20% 给管理/监控连接
• 避免突发流量打满

✅ 最终建议:
• HikariCP maximum-pool-size: 50-80
• PgBouncer default_pool_size: 20-30
• 总服务端连接 ≈ 80 * 4 = 320 < PostgreSQL max_connections(500)

Node.js (pg + pg-pool)

const { Pool } = require('pg');

const pool = new Pool({
  host: '192.168.1.100',  // PgBouncer VIP
  port: 6432,
  database: 'mydb',
  user: 'app_user',
  password: 'your_password',
  max: 20,  // 应用层池大小
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

// 使用连接池
const client = await pool.connect();
try {
  const result = await client.query('SELECT * FROM users WHERE id = $1', [1]);
  console.log(result.rows);
} finally {
  client.release();
}

🔒 安全加固配置(生产环境必备)

; /etc/pgbouncer/pgbouncer.ini - 安全增强

[pgbouncer]
; 限制管理接口访问来源
admin_users = pgbouncer_admin
stats_users = pgbouncer_monitor

; 启用客户端 TLS(应用→PgBouncer)
client_tls_sslmode = allow ; 或 require(强制加密)
client_tls_cert_file = /etc/ssl/certs/pgbouncer.crt
client_tls_key_file = /etc/ssl/private/pgbouncer.key

; 启用服务端 TLS(PgBouncer→PostgreSQL)
server_tls_sslmode = verify-full ; 验证后端证书
server_tls_ca_file = /etc/ssl/certs/ca.crt
server_tls_ciphers = high:!aNULL:!MD5

; 日志配置(避免记录敏感信息)
log_connections = 1
log_disconnections = 1
; 不要开启 debug 级别,可能泄露密码

防火墙规则:

# 仅允许内网访问 6432 端口
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="6432" protocol="tcp" accept' --permanent
sudo firewall-cmd --reload

📋 日志配置与轮转

; /etc/pgbouncer/pgbouncer.ini - 日志配置
logfile = /var/log/pgbouncer/pgbouncer.log
syslog = 0 ; 0=禁用,1=启用(配合 rsyslog)
syslog_ident = pgbouncer
syslog_facility = daemon

日志轮转配置:

# /etc/logrotate.d/pgbouncer
/var/log/pgbouncer/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    postrotate
        /bin/kill -HUP `cat /var/run/pgbouncer/pgbouncer.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

📋 配置变更流程(生产环境运维规范)

1. 预验证

# 语法检查(不启动)
pgbouncer -d /etc/pgbouncer/pgbouncer.ini --check

2. 灰度发布

  • 先在一台从节点部署新配置
  • 观察 5-10 分钟监控指标(连接等待数、错误率)

3. 全量滚动

逐个节点 RELOAD(不中断连接):

psql -h /tmp -p 6432 -U pgbouncer_admin pgbouncer -c "RELOAD;"

4. 回滚预案

# 保留上一版本配置文件
cp /etc/pgbouncer/pgbouncer.ini /etc/pgbouncer/pgbouncer.ini.bak.$(date +%Y%m%d)

# 如异常,快速恢复
cp /etc/pgbouncer/pgbouncer.ini.bak.* /etc/pgbouncer/pgbouncer.ini
psql -h /tmp -p 6432 -U pgbouncer_admin pgbouncer -c "RELOAD;"

下篇预告

上篇讲了安装配置和核心原理,下篇我们将深入:

  • 📊 实战压测:PgBouncer 性能提升实测数据
  • 🐛 避坑指南:临时表、预编译语句、LISTEN/NOTIFY 等 5 个大坑
  • 📈 监控告警:Prometheus + Grafana 完整配置
  • 🔍 故障排查:日志分析 + 常见问题诊断
  • ❓ FAQ:10 个高频问题解答
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐