PgBouncer 实战:用连接池挡住 PostgreSQL 连接风暴

PgBouncer 不读 PostgreSQL 的 pg_authid,它需要自己的用户清单。最简单的方式是用 md5 密码,密码串由「密码 + 用户名」拼出:

-- 在 PostgreSQL 里生成与 PgBouncer 兼容的 md5 串
SELECT 'md5' || md5('your_password' || 'appuser');

-- userlist.txt 内容(用户名 + 空格 + 引号包裹的 md5 串)
"appuser" "md5893b..."

如果后端使用了 SCRAM 认证(PostgreSQL 13+ 默认),可以把 auth_type 设为 scram-sha-256 并配合 auth_file 里的 scram 密码,或用 auth_user 让 PgBouncer 以代理身份动态查询 pg_shadow。生产环境推荐后者,避免明文维护密码清单。

六、监控与排障:SHOW 命令

PgBouncer 自带一套虚拟管理库,连上 6432 端口、用 admin_users 里的账号即可实时查看池状态:

psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW POOLS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW STATS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW CLIENTS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW SERVERS;"

关键看 SHOW POOLS 的 cl_active / cl_waiting / sv_active:cl_waiting 持续大于 0 说明前端在排队,通常是 default_pool_size 太小或后端慢查询堆积;sv_active 长期打满则说明后端 PostgreSQL 本身成为瓶颈,此时加 PgBouncer 无济于事,要先去查慢 SQL。

配合 PostgreSQL 侧的实时监控,能快速定位是「连接太多」还是「查询太慢」:

SELECT state, count(*) FROM pg_stat_activity GROUP BY state ORDER BY 2 DESC;
SELECT pid, state, wait_event_type, query FROM pg_stat_activity
WHERE state <> 'idle' ORDER BY query_start LIMIT 20;

七、常见误区对照

误区正确做法
transaction 模式下用临时表临时表是会话级,事务结束即丢;改用普通表或应用内缓存
default_pool_size 设得很大它按库×用户计,过大反而压垮 PostgreSQL
PgBouncer 能解决慢 SQL它只收敛连接,后端瓶颈要靠索引与慢查询治理
忘了 server_reset_querytransaction 模式务必设 DISCARD ALL,避免会话状态泄漏

八、与 PostgreSQL 协同调优

引入 PgBouncer 后,PostgreSQL 的 max_connections 可以大胆调小(例如 200),把「海量前端连接」的缓冲任务交给 PgBouncer。这样每个后端连接都能分到更多 work_mem,复杂查询反而更快。一个经验公式:

PostgreSQL max_connections ≈ sum(default_pool_size × 库用户组合数) + 预留(20~50)
PgBouncer default_pool_size × 库用户数  ≪  PostgreSQL max_connections

例如 1 个库、1 个应用用户、default_pool_size=20,则后端稳定 20 条连接;前端哪怕来 5000 个并发连接,PostgreSQL 也只看到 20 条,内存与上下文切换开销骤降。

九、落地清单

步骤动作校验
1选 pool_mode=transaction确认无会话级临时表/SET 依赖
2部署 PgBouncer(Docker 或二进制)6432 端口可达
3配置 userlist / auth_user应用用原账号能登录
4设 default_pool_size,调小 PG max_connectionsSHOW POOLS 无 cl_waiting
5接监控(SHOW STATS + pg_stat_activity)告警 cl_waiting / sv_active

PgBouncer 是 PostgreSQL 高并发架构里性价比最高的组件之一:部署简单、零侵入、收益立竿见影。当你在面对连接风暴、内存被连接吃光、或想把微服务架构下的数据库连接数收敛下来时,它几乎总是第一选择。延伸阅读可参考本站 PostgreSQL 慢查询排查、PostgreSQL 备份与 PITR 恢复、PostgreSQL JSONB 实战,以及用 Docker 编排 与 VS Code 远程开发 搭建本地验证环境。

下面是一份生产可参考的 pgbouncer.ini,关键参数我都加了注释:

[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 5000
default_pool_size = 20
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
server_idle_timeout = 60
server_lifetime = 3600
server_reset_query = DISCARD ALL
log_connections = 1
log_disconnections = 1
admin_users = pgb_admin
stats_users = pgb_stats
参数含义建议值
default_pool_size每个用户+数据库组合保留的后端连接数20(按并发调)
max_client_connPgBouncer 接受的前端连接上限5000+
reserve_pool_size主池满时临时借用的额外连接5
server_idle_timeout空闲后端连接回收时间(秒)60
server_lifetime后端连接最大存活时间(秒),防长连接老化3600

一个常见误区是 default_pool_size 设得过大——它本质是「每个库每用户」的后端连接数,若设成 100、又有 10 个库,后端实际连接会瞬间破千,反而把 PostgreSQL 打爆。原则是让 default_pool_size × 库用户数 远小于 PostgreSQL 的 max_connections。

五、认证配置 userlist.txt

PgBouncer 不读 PostgreSQL 的 pg_authid,它需要自己的用户清单。最简单的方式是用 md5 密码,密码串由「密码 + 用户名」拼出:

-- 在 PostgreSQL 里生成与 PgBouncer 兼容的 md5 串
SELECT 'md5' || md5('your_password' || 'appuser');

-- userlist.txt 内容(用户名 + 空格 + 引号包裹的 md5 串)
"appuser" "md5893b..."

如果后端使用了 SCRAM 认证(PostgreSQL 13+ 默认),可以把 auth_type 设为 scram-sha-256 并配合 auth_file 里的 scram 密码,或用 auth_user 让 PgBouncer 以代理身份动态查询 pg_shadow。生产环境推荐后者,避免明文维护密码清单。

六、监控与排障:SHOW 命令

PgBouncer 自带一套虚拟管理库,连上 6432 端口、用 admin_users 里的账号即可实时查看池状态:

psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW POOLS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW STATS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW CLIENTS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW SERVERS;"

关键看 SHOW POOLS 的 cl_active / cl_waiting / sv_active:cl_waiting 持续大于 0 说明前端在排队,通常是 default_pool_size 太小或后端慢查询堆积;sv_active 长期打满则说明后端 PostgreSQL 本身成为瓶颈,此时加 PgBouncer 无济于事,要先去查慢 SQL。

配合 PostgreSQL 侧的实时监控,能快速定位是「连接太多」还是「查询太慢」:

SELECT state, count(*) FROM pg_stat_activity GROUP BY state ORDER BY 2 DESC;
SELECT pid, state, wait_event_type, query FROM pg_stat_activity
WHERE state <> 'idle' ORDER BY query_start LIMIT 20;

七、常见误区对照

误区正确做法
transaction 模式下用临时表临时表是会话级,事务结束即丢;改用普通表或应用内缓存
default_pool_size 设得很大它按库×用户计,过大反而压垮 PostgreSQL
PgBouncer 能解决慢 SQL它只收敛连接,后端瓶颈要靠索引与慢查询治理
忘了 server_reset_querytransaction 模式务必设 DISCARD ALL,避免会话状态泄漏

八、与 PostgreSQL 协同调优

引入 PgBouncer 后,PostgreSQL 的 max_connections 可以大胆调小(例如 200),把「海量前端连接」的缓冲任务交给 PgBouncer。这样每个后端连接都能分到更多 work_mem,复杂查询反而更快。一个经验公式:

PostgreSQL max_connections ≈ sum(default_pool_size × 库用户组合数) + 预留(20~50)
PgBouncer default_pool_size × 库用户数  ≪  PostgreSQL max_connections

例如 1 个库、1 个应用用户、default_pool_size=20,则后端稳定 20 条连接;前端哪怕来 5000 个并发连接,PostgreSQL 也只看到 20 条,内存与上下文切换开销骤降。

九、落地清单

步骤动作校验
1选 pool_mode=transaction确认无会话级临时表/SET 依赖
2部署 PgBouncer(Docker 或二进制)6432 端口可达
3配置 userlist / auth_user应用用原账号能登录
4设 default_pool_size,调小 PG max_connectionsSHOW POOLS 无 cl_waiting
5接监控(SHOW STATS + pg_stat_activity)告警 cl_waiting / sv_active

PgBouncer 是 PostgreSQL 高并发架构里性价比最高的组件之一:部署简单、零侵入、收益立竿见影。当你在面对连接风暴、内存被连接吃光、或想把微服务架构下的数据库连接数收敛下来时,它几乎总是第一选择。延伸阅读可参考本站 PostgreSQL 慢查询排查、PostgreSQL 备份与 PITR 恢复、PostgreSQL JSONB 实战,以及用 Docker 编排 与 VS Code 远程开发 搭建本地验证环境。

当你把应用从单体拆成微服务,或把定时任务从每天一次改成每秒一次,PgBouncer 往往是 PostgreSQL 最后一道防线。PostgreSQL 采用「一连接一进程」模型,每建立一个连接就要 fork 一个后端进程、分配约 5–10MB 内存,连接数一旦突破数百,连接风暴就会让数据库 CPU 飙升、响应变慢甚至 OOM。本文用一组可直接抄的配置,讲清如何用 PostgreSQL 连接池把成百上千的前端连接收敛到少量后端连接上,让数据库只看到稳定、可控的连接数。

一、为什么 PostgreSQL 需要连接池

PostgreSQL 每来一个客户端连接,服务端就 fork 一个独立后端进程。连接创建成本包含:身份验证、内存分配(work_mem、各类缓冲区)、上下文切换。实测在 2C4G 的实例上,把 max_connections 调到 1000,仅空闲连接就可能吃掉数 GB 内存,真正留给查询的内存反而被挤压。

而应用端往往倾向于「多开连接保平安」:Java 的 HikariCP、Python 的 SQLAlchemy、Node 的 pg 模块默认都倾向于维持较大的连接池。当流量抖动、超时重试叠加,瞬间产生大量短连接,数据库来不及回收,就会陷入连接风暴——典型症状就是 Too many connections 报错、pg_stat_activity 里堆满 idle in transaction。

PgBouncer 位于应用与数据库之间,把 N 个前端连接映射到 M 个后端连接(N≫M),让数据库侧只维持少量稳定连接。它自身是单进程、事件驱动的轻量代理,内存占用极低,一台 1G 内存的小机器就能轻松挡住上万个前端连接。

二、PgBouncer 的三种池化模式

PgBouncer 的核心差异在于「后端连接何时归还给池」,对应三种模式,直接决定你能不能用事务、临时表、SET 变量等会话级特性。

模式连接复用粒度事务语义适用场景
session整个会话期间独占一条后端连接完整,支持临时表、SET、序列等依赖会话级状态的传统应用
transaction一个事务结束立即归还仅在单事务内有效绝大多数无状态 Web 应用(推荐)
statement单条语句结束即归还不能跨语句保持事务特殊只读/无状态查询(极少用)

2.1 Session 模式

客户端连上后独占一条后端连接,直到断开。行为与直连数据库完全一致,但失去了连接复用能力,性能提升有限。只有在应用大量使用临时表、会话级 SET、自定义 GUC 变量、LISTEN/NOTIFY 时才需要它。

2.2 Transaction 模式(推荐)

一个事务提交或回滚后,后端连接立即归还池中供其他客户端复用。这是 Web 应用的黄金选择:绝大多数请求都是「建连→执行一条或几条 SQL→提交→断开」的短事务,复用率极高。代价是不能在事务外使用临时表、不能跨语句保持 SET 变量。

2.3 Statement 模式

每条语句执行完就归还连接,要求事务中同一时刻只能有一条语句在跑,因此基本无法使用多语句事务。仅适合极特殊的读多写少、完全无状态的场景,日常几乎不用。

三、用 Docker 快速起一个 PgBouncer

最省事的方式是用官方镜像起一个容器,把应用连接串从直连 5432 改成指向 PgBouncer 的 6432。下面是一个 docker-compose 示例:

services:
  pgbouncer:
    image: edoburu/pgbouncer:1.22
    environment:
      - DATABASES=mydb=host.docker.internal port=5432 dbname=mydb
      - POOL_MODE=transaction
      - MAX_CLIENT_CONN=5000
      - DEFAULT_POOL_SIZE=20
      - AUTH_TYPE=md5
    ports:
      - "6432:6432"
    restart: unless-stopped

应用侧的 JDBC/连接串只需把端口改成 6432、host 指向 PgBouncer 即可,账号密码保持不变。注意 AUTH_TYPE 要与后端 PostgreSQL 的密码加密方式匹配,否则会出现密码认证失败。

四、核心配置参数解析

下面是一份生产可参考的 pgbouncer.ini,关键参数我都加了注释:

[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 5000
default_pool_size = 20
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
server_idle_timeout = 60
server_lifetime = 3600
server_reset_query = DISCARD ALL
log_connections = 1
log_disconnections = 1
admin_users = pgb_admin
stats_users = pgb_stats
参数含义建议值
default_pool_size每个用户+数据库组合保留的后端连接数20(按并发调)
max_client_connPgBouncer 接受的前端连接上限5000+
reserve_pool_size主池满时临时借用的额外连接5
server_idle_timeout空闲后端连接回收时间(秒)60
server_lifetime后端连接最大存活时间(秒),防长连接老化3600

一个常见误区是 default_pool_size 设得过大——它本质是「每个库每用户」的后端连接数,若设成 100、又有 10 个库,后端实际连接会瞬间破千,反而把 PostgreSQL 打爆。原则是让 default_pool_size × 库用户数 远小于 PostgreSQL 的 max_connections。

五、认证配置 userlist.txt

PgBouncer 不读 PostgreSQL 的 pg_authid,它需要自己的用户清单。最简单的方式是用 md5 密码,密码串由「密码 + 用户名」拼出:

-- 在 PostgreSQL 里生成与 PgBouncer 兼容的 md5 串
SELECT 'md5' || md5('your_password' || 'appuser');

-- userlist.txt 内容(用户名 + 空格 + 引号包裹的 md5 串)
"appuser" "md5893b..."

如果后端使用了 SCRAM 认证(PostgreSQL 13+ 默认),可以把 auth_type 设为 scram-sha-256 并配合 auth_file 里的 scram 密码,或用 auth_user 让 PgBouncer 以代理身份动态查询 pg_shadow。生产环境推荐后者,避免明文维护密码清单。

六、监控与排障:SHOW 命令

PgBouncer 自带一套虚拟管理库,连上 6432 端口、用 admin_users 里的账号即可实时查看池状态:

psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW POOLS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW STATS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW CLIENTS;"
psql -h 127.0.0.1 -p 6432 -U pgb_admin pgbouncer -c "SHOW SERVERS;"

关键看 SHOW POOLS 的 cl_active / cl_waiting / sv_active:cl_waiting 持续大于 0 说明前端在排队,通常是 default_pool_size 太小或后端慢查询堆积;sv_active 长期打满则说明后端 PostgreSQL 本身成为瓶颈,此时加 PgBouncer 无济于事,要先去查慢 SQL。

配合 PostgreSQL 侧的实时监控,能快速定位是「连接太多」还是「查询太慢」:

SELECT state, count(*) FROM pg_stat_activity GROUP BY state ORDER BY 2 DESC;
SELECT pid, state, wait_event_type, query FROM pg_stat_activity
WHERE state <> 'idle' ORDER BY query_start LIMIT 20;

七、常见误区对照

误区正确做法
transaction 模式下用临时表临时表是会话级,事务结束即丢;改用普通表或应用内缓存
default_pool_size 设得很大它按库×用户计,过大反而压垮 PostgreSQL
PgBouncer 能解决慢 SQL它只收敛连接,后端瓶颈要靠索引与慢查询治理
忘了 server_reset_querytransaction 模式务必设 DISCARD ALL,避免会话状态泄漏

八、与 PostgreSQL 协同调优

引入 PgBouncer 后,PostgreSQL 的 max_connections 可以大胆调小(例如 200),把「海量前端连接」的缓冲任务交给 PgBouncer。这样每个后端连接都能分到更多 work_mem,复杂查询反而更快。一个经验公式:

PostgreSQL max_connections ≈ sum(default_pool_size × 库用户组合数) + 预留(20~50)
PgBouncer default_pool_size × 库用户数  ≪  PostgreSQL max_connections

例如 1 个库、1 个应用用户、default_pool_size=20,则后端稳定 20 条连接;前端哪怕来 5000 个并发连接,PostgreSQL 也只看到 20 条,内存与上下文切换开销骤降。

九、落地清单

步骤动作校验
1选 pool_mode=transaction确认无会话级临时表/SET 依赖
2部署 PgBouncer(Docker 或二进制)6432 端口可达
3配置 userlist / auth_user应用用原账号能登录
4设 default_pool_size,调小 PG max_connectionsSHOW POOLS 无 cl_waiting
5接监控(SHOW STATS + pg_stat_activity)告警 cl_waiting / sv_active

PgBouncer 是 PostgreSQL 高并发架构里性价比最高的组件之一:部署简单、零侵入、收益立竿见影。当你在面对连接风暴、内存被连接吃光、或想把微服务架构下的数据库连接数收敛下来时,它几乎总是第一选择。延伸阅读可参考本站 PostgreSQL 慢查询排查、PostgreSQL 备份与 PITR 恢复、PostgreSQL JSONB 实战,以及用 Docker 编排 与 VS Code 远程开发 搭建本地验证环境。

上一篇 uv 包管理器实战:Python 依赖与虚拟环境提速
下一篇 Terraform 管理阿里云 ECS:基础设施即代码实战
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]