PostgreSQL 连接池实战(上):PgBouncer 安装配置与核心原理(生产环境版)
📋 文章信息
• 适用版本: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:
- 应用连接数 > 200
- 短连接频繁(HTTP 请求式查询)
- 需要连接数隔离(不同业务不同池)
- 高可用切换时需要快速重连
🚀 快速安装(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
每个数据库 + 用户组合的服务端连接数上限。
推荐调优方法(按优先级):
- 基准压测:从
pool_size=10开始,逐步增加,观察 TPS/延迟变化 - 监控指标:当
SHOW POOLS中cl_waiting持续 > 0 时,考虑扩容 - 经验参考(仅作起点):
- OLTP 短查询:
pool_size = CPU 核数 × 3~5 - 混合负载:
pool_size = CPU 核数 × 2~3 - 复杂查询:
pool_size = CPU 核数 × 1~2(避免过多并发消耗内存)
- OLTP 短查询:
核心原则:服务端连接数 ≈ 能充分利用 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 个高频问题解答
更多推荐
所有评论(0)