pgBouncer 连接池
High Contrast
Dark Mode
Light Mode
Sepia
Forest
1 min read204 words

pgBouncer 连接池

PostgreSQL 每个连接都是独立进程,建立连接需要 5-10ms,维护每个连接约消耗 5-10MB 内存。当应用有 500 个并发连接时,光连接管理就会消耗 2.5-5GB 内存。pgBouncer 是轻量级连接池,能把数百个应用连接复用到少量数据库连接,大幅降低 PostgreSQL 负担。


为什么需要连接池

没有连接池:
应用服务器 (100 个请求) → 100 个 PostgreSQL 进程 → 1GB+ 内存
有 pgBouncer:
应用服务器 (100 个请求) → pgBouncer (10-20 个池连接) → PostgreSQL 进程
↑ 连接复用,只用 100-200MB 内存
连接建立时间:
直连 PostgreSQL:5-10ms(每次请求都要建连接)
通过 pgBouncer:< 0.1ms(从池中取现有连接)

安装 pgBouncer

# Ubuntu/Debian
sudo apt-get install pgbouncer
# macOS
brew install pgbouncer
# Docker
docker run -d \
--name pgbouncer \
-p 6432:6432 \
-v /etc/pgbouncer:/etc/pgbouncer \
edoburu/pgbouncer

核心配置文件

pgbouncer.ini

[databases]
; 数据库别名配置
; 格式:别名 = host=... port=... dbname=...
myapp = host=127.0.0.1 port=5432 dbname=myapp_production
myapp_ro = host=replica.db.internal port=5432 dbname=myapp_production
[pgbouncer]
; 监听配置
listen_addr = 0.0.0.0
listen_port = 6432
; 认证方式
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
; 连接池模式(核心配置,见下方说明)
pool_mode = transaction  ; 推荐:事务级复用
; 连接数配置
max_client_conn = 1000   ; 最大客户端连接数
default_pool_size = 25   ; 每个 database×user 组合的池大小
; 超时配置
client_idle_timeout = 600    ; 空闲客户端超时(秒)
server_idle_timeout = 600    ; 空闲服务器连接超时
server_connect_timeout = 15  ; 连接 PostgreSQL 超时
query_timeout = 0            ; 查询超时(0=禁用)
; 管理接口
admin_users = postgres
stats_users = monitoring_user
; 日志
logfile = /var/log/pgbouncer/pgbouncer.log
pidfile = /var/run/pgbouncer/pgbouncer.pid
; TLS(生产环境推荐)
; client_tls_sslmode = require
; client_tls_cert_file = /etc/ssl/certs/pgbouncer.crt
; client_tls_key_file = /etc/ssl/private/pgbouncer.key

userlist.txt(用户认证文件)

# 格式:"用户名" "密码哈希"
# 获取密码哈希:SELECT usename, passwd FROM pg_shadow;
"app_user" "SCRAM-SHA-256$4096:..."
"readonly_user" "SCRAM-SHA-256$4096:..."

三种池模式

session(会话模式):
客户端连接 → 独占一个服务器连接,直到断开
复用率最低,几乎无意义
✅ 适合:使用 SET、临时表等需要会话状态的场景
transaction(事务模式):
客户端在事务期间占用服务器连接,事务结束立即归还
复用率高(推荐)
❌ 不支持:prepared statements(需要用 protocol_prepared_statements=0)
❌ 不支持:LISTEN/NOTIFY、Advisory Locks(会话级)
✅ 适合:大多数 Web 应用
statement(语句模式):
每条 SQL 完成后立即归还连接
复用率最高,但限制很多
❌ 不支持:多语句事务
✅ 适合:只有单语句操作的场景(极少用)

应用层连接到 pgBouncer

# Python (psycopg2):连接 pgBouncer 而不是直接连 PostgreSQL
import psycopg2
# 原来直连 PostgreSQL:
# conn = psycopg2.connect(host="db.internal", port=5432, ...)
# 改为通过 pgBouncer:
conn = psycopg2.connect(
host="pgbouncer.internal",
port=6432,         # pgBouncer 端口
database="myapp",  # pgbouncer.ini 中定义的别名
user="app_user",
password="password",
# 事务模式下禁用 prepared statements:
options="-c statement_cache_size=0"
)
// Node.js (pg):
const { Pool } = require('pg');
const pool = new Pool({
host: 'pgbouncer.internal',
port: 6432,
database: 'myapp',
user: 'app_user',
password: 'password',
// 应用层连接池设小一些,pgBouncer 负责实际池化
max: 10,  // 应用层最多 10 个连接到 pgBouncer
});

监控 pgBouncer

-- 连接到 pgBouncer 的管理数据库
psql -h pgbouncer.internal -p 6432 -U postgres pgbouncer
-- 查看连接池状态
SHOW POOLS;
-- 输出:
-- database | user     | cl_active | cl_waiting | sv_active | sv_idle | sv_used
-- myapp    | app_user | 45        | 2          | 20        | 5       | 0
--
-- cl_active: 正在使用服务器连接的客户端数
-- cl_waiting: 等待服务器连接的客户端数(如果持续 > 0,说明池太小)
-- sv_active: 正在使用的服务器连接数
-- sv_idle: 空闲的服务器连接数
-- sv_used: 刚用完、等待清理的服务器连接
-- 查看统计信息
SHOW STATS;
-- total_requests: 总请求数
-- total_query_time: 总查询时间(微秒)
-- avg_query_time: 平均查询时间
-- 查看客户端连接
SHOW CLIENTS;
-- 查看服务器连接
SHOW SERVERS;
-- 重新加载配置(不中断连接)
RELOAD;
-- 暂停/恢复连接池(维护时使用)
PAUSE myapp;
RESUME myapp;

性能对比与调优建议

-- 场景:Web 应用,每秒 500 个请求,每请求 3 个查询
-- 每个请求约 10ms
-- 无连接池(直连):
-- 峰值需要约 150-200 个并发连接
-- PostgreSQL 内存占用:200 × 10MB = 2GB(仅连接)
-- 连接建立开销:每次请求 5-10ms
-- 使用 pgBouncer(transaction 模式):
-- default_pool_size = 30(30 个服务器连接)
-- PostgreSQL 内存占用:30 × 10MB = 300MB
-- 连接获取时间:< 0.1ms
-- 吞吐量:几乎不变(I/O 仍是瓶颈)
; 调优建议:
; 1. default_pool_size = max_worker_processes(通常 8-16)× 应用实例数
; 2. max_client_conn 设为应用最大并发 × 2(留余量)
; 3. 监控 cl_waiting > 0 持续出现时,增大 pool_size
; 4. query_timeout 设为 30000(30秒),防止慢查询堆积

下一节postgresql.conf 关键参数调优——pgBouncer 解决了连接数问题,接下来调优 shared_bufferswork_memmax_connections 等 PostgreSQL 核心参数,让数据库本身跑得更快。