概述
你跑过几个服务,每个服务都要连同一个PostgreSQL数据库?PostgreSQL默认max_connections只有100,加上保留连接,实际可用也就90个左右。几个应用一挤,数据库直接拒绝新连接,报错"too many connections for role"。
PgBouncer是一个轻量级PostgreSQL连接池。它站在数据库前面,把多个客户端的连接复用成少量数据库连接。配置简单,资源占用几乎可以忽略,特别适合VPS环境。
准备工作
- 一台运行Ubuntu 22.04+或Debian 12+的VPS
- Docker和Docker Compose已安装
- 一个正在运行的PostgreSQL实例(端口默认5432)
- 能访问该PostgreSQL的数据库账号(建议用专用连接池账号)
如果还没装Docker,跑一行:
curl -fsSL https://get.docker.com | sudo sh
步骤
第一步:创建PgBouncer配置目录
mkdir -p ~/pgbouncer/conf ~/pgbouncer/data
第二步:写pgbouncer.ini配置文件
cat <<'EOF' | sudo tee ~/pgbouncer/conf/pgbouncer.ini
[databases]
# 格式:数据库名 = host:port
mydb = host=127.0.0.1 port=5432 dbname=mydb
[pgbouncer]
# 监听地址和端口
listen_addr = 0.0.0.0
listen_port = 6432
# PostgreSQL管理员密码(用于SHOW STATS等管理命令)
admin_users = pgbouncer
stats_users = pgbouncer
# 连接池模式:session/transaction/query
# transaction最常用,一个连接同时服务多个客户端
pool_mode = transaction
# 最大连接数(数据库侧)
max_connections = 50
# 每个池的最大连接数
default_pool_size = 10
# 客户端超时(秒),0表示不限制
client_idle_timeout = 0
client_login_timeout = 60
# 日志
log_connections = 1
log_disconnections = 1
logfile = /var/log/pgbouncer/pgbouncer.log
# 管理员密码文件
auth_file = /etc/pgbouncer/userlist.txt
auth_type = md5
EOF
第三步:创建用户认证文件
# 生成密码哈希:md5("密码+用户名")
PGPASSWORD=your_password psql -h 127.0.0.1 -U postgres -d mydb -tAc "SELECT '\"pgbouncer\" '||md5('your_password')" > /dev/null
# 更简单的方法:直接用md5sum
echo '"pgbouncer" "md5'$(echo -n "your_password+pgbouncer" | md5sum | awk '{print $1}')'"' > ~/pgbouncer/conf/userlist.txt
echo '"mydb_user" "md5'$(echo -n "db_password+mydb_user" | md5sum | awk '{print $1}')'"' >> ~/pgbouncer/conf/userlist.txt
实际使用中,更推荐在PostgreSQL里创建专用账号:
# 在PostgreSQL里执行
CREATE USER pgbouncer WITH PASSWORD 'your_password';
GRANT PgBouncer TO pgbouncer; -- PostgreSQL 16+
-- 或者PG15及以下:
GRANT SELECT ON pg_stat_statements TO pgbouncer;
第四步:用Docker Compose启动PgBouncer
cat <<'EOF' | sudo tee ~/pgbouncer/docker-compose.yml
version: '3.8'
services:
pgbouncer:
image: edoburu/pgbouncer:1.22.1
container_name: pgbouncer
restart: unless-stopped
ports:
- "6432:6432"
volumes:
- ./conf/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro
- ./conf/userlist.txt:/etc/pgbouncer/userlist.txt:ro
environment:
- DATABASE_URL=postgres://mydb_user:db_password@postgres:5432/mydb?sslmode=disable
depends_on:
- postgres
networks:
- pgb_net
networks:
pgb_net:
driver: bridge
EOF
把
postgres换成你实际PostgreSQL容器的主机名或IP。如果PostgreSQL在宿主机上,用host.docker.internal(Docker Desktop)或直接用宿主机IP。
第五步:启动服务
cd ~/pgbouncer
docker compose up -d
查看日志确认启动正常:
docker compose logs -f pgbouncer
看到类似输出就说明OK:
2026-08-10 13:51:00.123 UTC [1] LOG file descriptor: 10
2026-08-10 13:51:00.124 UTC [1] LOG paused: 0, in: 0, out: 0
2026-08-10 13:51:00.125 UTC [1] LOG loaded 2 connections
第六步:客户端连接方式变更
以前连数据库:
psql -h 192.168.1.100 -p 5432 -U mydb_user -d mydb
现在改连PgBouncer端口:
psql -h 192.168.1.100 -p 6432 -U mydb_user -d mydb
应用层的数据库连接地址也改一下端口号即可,其他不用变。
验证
# 1. 检查PgBouncer进程状态
docker compose ps pgbouncer
# 2. 查看连接池统计(需要psql连6432端口)
psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -c "SHOW STATS;"
# 3. 查看当前活跃连接
psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -c "SHOW CLIENTS;"
# 4. 查看池状态
psql -h 127.0.0.1 -p 6432 -U pgbouncer -d pgbouncer -c "SHOW POOLS;"
SHOW POOLS输出示例:
database | user | cl_active | cl_waiting | sv_active | sv_idle | sv_used | sv_tested | sv_login | maxwait
-----------+--------+-----------+------------+-----------+---------+---------+-----------+----------+-------
mydb | mydb_user | 3 | 0 | 2 | 8 | 0 | 0 | 0 | 0
cl_active:当前活跃的客户端连接数sv_active:当前活跃的数据库侧连接数sv_idle:空闲的数据库连接,可以随时复用
如果cl_waiting不为0,说明客户端在排队等连接,需要调大default_pool_size。
常见问题
Q:连接报错"FATAL: no pg_hba.conf entry for host"
PgBouncer连PostgreSQL时,需要在PostgreSQL的pg_hba.conf里放行PgBouncer所在IP:
# 在PostgreSQL服务器上加一行
echo "host all all 172.18.0.0/16 md5" >> /var/lib/postgresql/data/pg_hba.conf
# 或者Docker方式
docker exec -it postgres sh -c "echo 'host all all 172.18.0.0/16 md5' >> data/pg_hba.conf"
docker exec -it postgres systemctl reload postgresql
Q:应用连上后查询报错"server closed the connection unexpectedly"
可能是pool_mode设置不当。如果应用用了事务外的连接(比如准备语句),transaction模式会有问题。试试改成session模式,但会占用更多数据库连接:
pool_mode = session
Q:怎么查看PgBouncer实时日志?
docker compose logs -f pgbouncer
# 或者看宿主机日志文件
tail -f ~/pgbouncer/logs/pgbouncer.log
Q:PgBouncer重启后连接断开怎么办?
PgBouncer重启会断开所有客户端连接。应用层需要有重连机制。大多数现代ORM和驱动都支持自动重连,检查一下配置里有没有reconnect=true或类似选项。
Q:max_connections设多少合适?
一个经验公式:max_connections = 数据库可用连接数 × 0.7。比如数据库max_connections=100,PgBouncer的max_connections设70左右比较安全,留30给管理操作和突发流量。
总结
PgBouncer配置简单,几行配置文件就能跑起来。核心就三件事:指定后端数据库地址、设置pool_mode、控制max_connections。把应用的数据库端口从5432改成6432,剩下的交给PgBouncer。
你的应用有没有遇到连接数不够的问题?试试看。