为何通过PgBouncer执行pgbench测试时,查询量提升TPS反而上升?
问题背景
使用Postgres 13搭配PgBouncer执行pgbench测试时发现:提升客户端连接数与单客户端查询数量(-t参数)时,TPS(每秒事务数)反而上升,与“负载提升TPS下降”的预期相反;但直接连接数据库端口(不经过PgBouncer)时,负载提升会导致性能下降甚至数据库崩溃。
测试数据
通过PgBouncer(端口6544)的测试结果
固定客户端连接数700、工作进程数8,调整单客户端查询数(-t):
# -t 50(只读模式-S) pgbench -U postgres -h .... -p 6544 datafactory -c 700 -j 8 -t 50 -S latency average = 1591.633 ms tps = 439.799988 (including connections establishing) tps = 442.163375 (excluding connections establishing) # -t 100 pgbench -U postgres -h ... -p 6544 datafactory -c 700 -j 8 -t 100 -S latency average = 1286.131 ms tps = 544.268178 (including connections establishing) tps = 545.953341 (excluding connections establishing) # -t 300 pgbench -U postgres -h ... -p 6544 datafactory -c 700 -j 8 -t 300 -S latency average = 1246.031 ms tps = 561.783731 (including connections establishing) tps = 562.399700 (excluding connections establishing)
降低查询数量时TPS骤降:
pgbench -U postgres -h .. -p 6544 datafactory -c 700 -j 8 -t 10 -S latency average = 8633.526 ms tps = 81.079273 (including connections establishing) tps = 81.465337 (excluding connections establishing)
直接连接Postgres(端口5433)的测试结果
固定客户端连接数250、工作进程数8,调整单客户端查询数:
# -t 500 pgbench -U postgres -h ... -p 5433 datafactory -c 250 -j 8 -t 500 latency average = 2155.394 ms tps = 115.988055 (including connections establishing) tps = 116.134037 (excluding connections establishing) # -t 700 pgbench -U postgres -h ... -p 5433 datafactory -c 250 -j 8 -t 700 latency average = 835.555 ms tps = 299.202467 (including connections establishing) tps = 299.228977 (excluding connections establishing)
直接连接时出现数据库崩溃:
pgbench -U postgres -h ... -p 5433 datafactory -c 250 -j 8 -t 1000 WARNING: terminating connection because of crash of another server process pgbench -U postgres -h ... -p 5433 datafactory -c 250 -j 8 -t 700 connection to database "datafactory" failed: FATAL: the database system is in recovery mode
PgBouncer配置
listen_port = 6544 listen_addr = '*' auth_type = scram-sha-256 auth_file = /etc/pgbouncer/userlist.txt auth_proxy = on auth_failure_threshold = 3 auth_inactivity_period = 60 auth_last_size = 10 log_audit = 1 logfile = /pgerrorlogs/tkldd-ldisu0001/pgbouncer.log pidfile = /var/run/pgbouncer/pgbouncer.pid admin_users = pgbouncer max_client_conn = 1000 pool_mode = transaction min_pool_size = 0 default_pool_size = 30 max_db_connections = 30 max_user_connections = 30 ignore_startup_parameters = extra_float_digits
原因分析
PgBouncer连接池的核心作用
配置中pool_mode = transaction(事务级连接池)且max_db_connections = 30,意味着不管客户端连接有多少(比如700),实际打到Postgres的连接数被严格控制在30个以内。Postgres基于进程模型,过多数据库连接会导致CPU上下文切换暴涨、内存耗尽,反而降低性能。PgBouncer通过连接池将大量客户端请求合并到少量数据库连接上,避免了连接风暴,让数据库能高效处理请求。查询数量(
-t)对开销的影响
当-t值很小时(比如10),每个客户端仅执行10次查询,连接建立、认证的开销占比极高——即使PgBouncer复用连接,客户端频繁的连接断开、请求初始化也会消耗大量资源,导致TPS无法提升。而当-t增大时,每个客户端的查询次数变多,连接复用的效率被充分发挥,初始化开销被分摊到更多事务上,TPS自然上升;当-t从100到300时TPS增长放缓,说明已经接近数据库的处理瓶颈。直接连接的性能崩溃逻辑
直接连接时,250个客户端会建立250个Postgres数据库连接,远超过Postgres的最佳连接数(通常为CPU核心数*2+1)。过多连接会引发:- CPU上下文切换频繁,每个连接的CPU时间片被严重压缩
- 内存占用剧增(每个Postgres连接需数MB内存),可能触发OOM导致进程崩溃
- 数据库锁竞争加剧,事务等待时间变长
这就是直接连接时性能下降甚至数据库崩溃的核心原因。
总结
PgBouncer的连接池机制规避了Postgres在高连接数下的性能劣化问题,而单客户端查询数量的提升充分利用了连接复用的优势,分摊了连接开销,因此TPS随查询数上升;直接连接时没有连接池的保护,过多数据库连接直接压垮Postgres,导致性能下降甚至崩溃。
内容的提问来源于stack exchange,提问作者Gerzzog

