正在加载,请稍候…

PgBouncer:将 PostgreSQL 扩展到 10,000+ 并发连接

掌握 PgBouncer 配置以实现高吞吐 PostgreSQL:事务池化、正确池大小、监控以及导致事故的生产陷阱。

PgBouncer:将 PostgreSQL 扩展到 10,000+ 并发连接

PgBouncer:将 PostgreSQL 扩展到 10,000+ 连接

PostgreSQL 为每个连接创建一个 OS 进程,每个进程消耗约 5-10MB RAM。PgBouncer 将数千个应用程序连接复用到一个小的数据库连接池中。

三种池化模式

会话池化:每个客户端会话对应一个服务器连接。支持 SET、咨询锁、LISTEN/NOTIFY。效率最低。适用于遗留应用。

事务池化(推荐):仅在事务期间分配服务器连接。效率提高 10-100 倍。无法使用会话级功能。

语句池化:每条语句后返回连接。仅自动提交。很少使用。

PgBouncer:将 PostgreSQL 扩展到 10,000+ 并发连接 插图

生产配置

[databases]
production = host=postgres.internal port=5432 dbname=appdb

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 5432
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt

pool_mode = transaction
default_pool_size = 25       # 每个数据库/用户对的服务器连接数
max_client_conn = 10000      # 最大应用程序连接数
reserve_pool_size = 5        # 用于突发流量的额外连接
max_db_connections = 100     # 每个数据库的总服务器连接数

server_connect_timeout = 15
client_idle_timeout = 600
server_idle_timeout = 600
server_lifetime = 3600

server_reset_query = DISCARD ALL

正确的池大小

公式:最佳连接数 = cpu_cores * 2(SSD)或 cpu_cores * 4(HDD)。超过此值,性能会因上下文切换开销而下降。

def calculate_pool_size(max_connections=200, num_instances=2, cpu_cores=8):
    available = max_connections - 3  # 为超级用户保留
    optimal = cpu_cores * 2
    per_instance = available // num_instances
    return {
        'pool_size': min(optimal, per_instance),
        'max_clients': per_instance * 200,
        'reserve': max(5, per_instance // 5),
    }
# 示例:8 CPU,200 max_connections,2 个实例
# 结果:pool_size=16, max_clients=2800

PgBouncer:将 PostgreSQL 扩展到 10,000+ 并发连接 插图

监控

psql -U pgbouncer -d pgbouncer

SHOW POOLS;
-- cl_waiting > 0:池已耗尽!客户端正在等待
-- maxwait > 100ms:连接延迟过高
-- sv_idle 高:池可能过大

SHOW STATS;  -- req/s,平均查询时间

RELOAD;            -- 无需重启即可应用配置
PAUSE production;  -- 用于维护
RESUME production;
services:
  pgbouncer-exporter:
    image: prometheuscommunity/pgbouncer-exporter:latest
    environment:
      DATA_SOURCE_NAME: "postgresql://pgbouncer:pass@localhost:5432/pgbouncer"
# 关键告警:
# cl_waiting > 0 持续 => 池已耗尽
# maxwait_seconds > 0.1 => 连接延迟过高

事务模式限制

# 错误:预处理语句
# EXECUTE 可能在不同于 PREPARE 的服务器连接上运行
# 修复:conn = psycopg3.connect(dsn, prepare_threshold=None)

# 错误:SET search_path = myschema
# 下一个查询可能使用默认 search_path 运行
# 修复:ALTER USER appuser SET search_path TO myschema;

# 错误:咨询锁(pg_advisory_lock/unlock 在不同连接上)
# 修复:使用 SELECT FOR UPDATE 或 Redis 分布式锁

# 错误:LISTEN/NOTIFY
# 修复:为 LISTEN 使用专用的非池化连接

PgBouncer:将 PostgreSQL 扩展到 10,000+ 并发连接 插图

高可用性

backend pgbouncer_pool
    mode tcp
    balance leastconn
    server pgbouncer1 10.0.0.1:5432 check
    server pgbouncer2 10.0.0.2:5432 check
    server pgbouncer3 10.0.0.3:5432 check

3 个 PgBouncer 实例 x 25 池大小 = 75 个总 PostgreSQL 连接,服务于 10,000+ 个应用程序连接。

故障排除指南

# "too many connections" 错误
psql -c "SELECT count(*) FROM pg_stat_activity;"
# 接近 max_connections:减少池大小或增加实例

# 切换到 PgBouncer 后查询变慢
psql -c "SHOW search_path;"
# 缺少会话配置:使用 ALTER USER/DATABASE 代替 SET

# 流量高峰期间池耗尽
watch -n1 'psql -U pgbouncer -d pgbouncer -c "SHOW POOLS;"'
# 增加 reserve_pool_size,减少 reserve_pool_timeout

PgBouncer 让一个 200 连接的 PostgreSQL 实例服务于数千个并发连接——这是可用基础设施变更中投资回报率最高的之一。