Reed's News
← 返回精选

PgBouncer 吞吐量提升 4 倍的工程实践

Tech 71 saisrirampur 2026/7/11 752 字 原文 ↗

PgBouncer 是单线程架构,单个进程仅占用一个 CPU 核心,无论服务器配备多少核心都是如此。以一台 16 核虚拟机为例,所有连接池工作都由这一个核心承担,其余 15 个核心完全闲置,而且远在 Postgres 达到性能上限前,PgBouncer 就会先出现吞吐量瓶颈。

在 ClickHouse 托管 Postgres 服务中,我们运行一组 PgBouncer 进程,数量与服务器可用核心数成正比。

所有进程均启用 so_reuseport 并绑定同一端口,由内核负责将入站连接均衡分配给各个进程。对客户端而言,只需连接单一端点,完全感知不到背后有多台 PgBouncer 在协同工作。这正是 PgBouncer 官方文档推荐的多核利用方案:进程本身保持单线程,通过 so_reuseport 让每一个核心都参与工作。

pgbouncer_jul2026_image5.png

问题点:查询取消机制

Postgres 的取消请求通过全新连接发送,携带专属取消密钥,与执行查询的连接相互独立。在 so_reuseport 模式下,内核可能将这个新连接分配给持有会话的进程之外的其他进程,导致取消请求被发送到一个完全不了解该查询的进程,最终无法生效。

我们通过进程间通信(peering)解决了这个问题:所有进程彼此感知,当取消请求被分配到错误进程时,会被转发至真正持有会话的进程。如此一来,无论请求抵达哪个进程,整个集群的查询取消功能都能正常工作。

我们采用事务模式进行连接池管理,事务提交后,服务器连接会立即归还至连接池。同时,连接配额会在集群中均分:max_client_conn(最大客户端连接数)和 max_db_connections(最大数据库连接数)会按进程数量拆分,确保整个集群不会超出 Postgres 的连接上限。

真实硬件环境测试

我们在相同的 AWS EC2 实例上对比两种配置:一台 16 核 c7i.4xlarge 作为连接池服务器,一台独立服务器运行 Postgres,第三台服务器通过 pgbench 发起只读事务负载,采用事务池模式。其中一台连接池服务器运行单个 PgBouncer 进程,另一台运行 16 个进程。实例类型、Postgres 配置、工作负载完全一致,唯一变量是进程数量。

我们将客户端连接数从 8 逐步增加到 256,同时测量吞吐量,以及连接池服务器实际使用的核心资源占比。

单个进程的吞吐量在约 8.7 万次事务/秒时达到峰值,之后负载越高性能越差,当连接数增至 256 时,吞吐量降至 7.7 万次事务/秒——所有资源都在争抢唯一的核心。而集群模式下吞吐量持续攀升至约 33.6 万次事务/秒,是单进程的 4 倍左右,这得益于更多核心的并行处理能力。

单进程模式下,资源利用始终局限于一个核心:负载高峰时,pidstat 显示 PgBouncer 进程 CPU 占用率稳定在约 97%,完全占满一个核心,但整台 16 核服务器的整体 CPU 利用率始终低于 10%。集群模式下,负载均匀分布在多个核心上,最多有约 8 个核心处于忙碌状态,直到 Postgres 和负载生成器成为性能瓶颈时,服务器仍有剩余资源可用。

保持 256 个客户端连接数不变:单进程服务器全程 CPU 利用率约为 9%,而集群服务器约为 52%。相同的实例类型、相同的 Postgres、相同的工作负载,一种配置让服务器大量资源闲置,另一种则充分发挥了硬件性能。

EC2 自带的 CloudWatch 监控指标从外部视角验证了这一结果:负载测试期间,单进程实例的平均 CPU 利用率约为 16%,集群实例约为 60%。CloudWatch 的数值比服务器内部监测结果略高,但两者的差距一致:在你付费购买的 16 核服务器上,单个 PgBouncer 几乎浪费了所有闲置核心。

连接上限的表现也类似。单进程模式下,max_client_conn 由单个进程独立执行,一旦超出上限,新客户端会被拒绝连接:

FATAL: no more connections allowed (max_client_conn)

而通过在集群中均分连接配额,既能提高整体连接上限,又能确保每个进程和 Postgres 都处于安全的资源限制范围内。

客户端连接数 单进程事务数/秒 单进程服务器CPU利用率 集群事务数/秒 集群服务器CPU利用率
8 8,910 0.8% 6,450 2.9%
32 54,203 5.2% 64,244 12.3%
64 86,570 8.3% 219,439 31.9%
128 83,463 8.1% 320,547 45.9%
256 76,893 7.7% 336,469 48.9%

在连接数较少时,单进程模式表现尚可,甚至略快——因为没有并行化需求,集群模式下的连接资源会被分散。但在真正关键的高并发场景下,单核心会成为无法突破的性能瓶颈,此时两种模式的差距会迅速拉开。

结论

单个 PgBouncer 是不错的默认选择,但当连接池而非 Postgres 成为吞吐量瓶颈时,就需要调整架构。根据核心数量配置集群、通过 so_reuseport 共享端口、借助进程间通信实现协同,能让连接池回归基础设施的定位,不再成为性能瓶颈。

所有 ClickHouse 托管 Postgres 服务器默认采用这一配置。创建一台 Postgres 实例即可体验这一架构的优势。

试用 ClickHouse 托管 Postgres

ClickHouse + Postgres 已成为高可扩展应用的统一数据栈。如今 ClickHouse Cloud 正式推出托管 Postgres 服务,让这一数据架构从项目启动之初就能为你所用。

立即注册