pgbench是PostgreSQL自带的基准测试工具,但它的测试结果高度依赖参数组合。很多性能结论之所以互相矛盾,并不是数据库本身差异大,而是测试时使用了完全不同的并发数、连接数、读写比例或事务脚本。如果不理解这些参数之间的相互影响,很容易把一次默认配置下的短时测试当成数据库的真实能力。本文从内置模型、并发与连接组合、读写比例和自定义脚本四个维度进行拆解,帮助读者建立一套可重复、贴近真实业务的测试方法。

pgbench默认使用TPC-B模型,它模拟的是银行转账场景。初始化时创建pgbench_accounts、pgbench_branches、pgbench_tellers和pgbench_history四张表,其中accounts表的数据量由缩放因子-scale决定。默认事务脚本会执行一条UPDATE accounts、一条SELECT abalance、一条INSERT history以及两条针对branches和tellers的UPDATE。这个模型本身并不复杂,但它的写入热点非常集中,几乎所有事务都会修改同一批账户行,因此并发越高,行锁竞争越激烈。
pgbench参数体系与默认测试模型的局限
要理解不同参数组合的差异,首先需要知道pgbench常用参数的含义。-c用于指定并发客户端数量,也就是同时连接到数据库的会话数;-j指定pgbench自身的工作线程数,一个线程可以调度多个客户端连接;-T指定测试时长,单位是秒;-M指定查询协议,可选simple、extended或prepared;-r会在测试结束后输出每条SQL语句的平均延迟;-P可以按固定时间间隔输出进度信息。初始化数据库使用-i,缩放因子通过-s设置。
下面是一个典型的默认读写测试命令。先初始化50倍缩放因子,再用10个并发客户端、2个线程测试60秒:
# 初始化测试数据 pgbench -i -s 50 postgres # 默认读写混合测试 pgbench -c 10 -j 2 -T 60 postgres
执行完成后会得到类似下面的输出:
transaction type: <builtin: TPC-B (sort of)> scaling factor: 50 query mode: simple number of clients: 10 number of threads: 2 duration: 60 s number of transactions actually processed: 184392 latency average = 3.252 ms initial connection time = 28.313 ms tps = 3073.194525 (without initial connection time) tps = 3072.228456 (including initial connection time)
默认模型最大的局限在于,UPDATE accounts语句访问的往往是同一批热点账户。当数据量远大于shared_buffers时,部分数据会落到磁盘,但如果测试库很小或者缓存足够大,几乎所有读操作都命中内存,TPS会明显偏高。另一个问题是默认脚本的写入模式比较单一,不能代表真实业务中常见的读多写少或批量写入场景。因此仅凭默认参数组合得出的结论,很难直接用于生产容量评估。
并发数、连接数与线程数的组合对比
很多测试只关注-c参数,把并发客户端从10调到100,却忽略了-j线程数的配合。实际上-j是pgbench内部的工作线程数,每个线程要负责调度若干个客户端连接。如果-j设置得太小,单个线程需要频繁在多个连接之间切换,pgbench自身反而可能成为压力瓶颈。反之,如果-j设置得过大,线程切换也会带来额外开销,而且测试压力可能超过真实应用能够产生的负载。
为了观察不同组合的差异,可以固定测试时长,按矩阵方式循环执行多组测试。下面示例分别测试并发数为10、50、100,线程数为1、2、4、8的组合:
for c in 10 50 100; do
for j in 1 2 4 8; do
echo "===== c=$c j=$j ====="
pgbench -c $c -j $j -T 120 -M prepared postgres
done
done
从典型测试数据来看,当并发客户端只有10个,线程数从1增加到4时,TPS提升幅度很小,可能只有2%到5%。但当并发数达到100时,线程数从1增加到8,吞吐量会有显著提升,同时平均延迟也会上升。以一台16核虚拟机为例,c=100、j=1时TPS大约为4200,平均延迟约23ms;同样c=100、j=8时TPS可以提升到6100左右,但平均延迟增加到约16ms。这说明高并发下增加工作线程确实能提高吞吐,但延迟并不一定会线性下降。
另一个容易忽略的参数是-C。使用-C会让pgbench每个事务都建立一次全新的数据库连接,用来模拟没有连接池的应用。此时结果中的including initial connection time会明显拉低TPS。例如c=50、j=4、-T 60且不使用-C时TPS为5800,加入-C后可能骤降到300以下,这反映的是连接建立开销而不是真实事务处理能力。生产环境通常使用连接池,因此测试时应根据实际架构决定是否加-C,不能默认忽略。
读写比例与自定义脚本参数组合
pgbench内置了只读和跳过更新两种简化模式。-S表示执行select-only脚本,只运行SELECT abalance;-N表示跳过默认脚本中的UPDATE accounts,保留INSERT history和SELECT。这两种模式可以快速模拟读多写少,但粒度仍然较粗。如果业务中读写比例为80%读、20%写,或者需要包含批量插入、复杂查询,就应该使用-f加载自定义脚本。
自定义脚本使用pgbench的脚本语法,以反斜杠开始命令。下面是一个模拟按账户随机更新并记录历史的脚本示例:
\set aid random(1, 100000) \set bid random(1, 1) \set tid random(1, 10) \set delta random(-5000, 5000) BEGIN; UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid; SELECT abalance FROM pgbench_accounts WHERE aid = :aid; INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP); END;
将脚本保存为workload.sql后,可以这样运行测试:
pgbench -f workload.sql -c 20 -j 4 -T 120 -M prepared postgres
只读模式测试出的TPS往往远高于含有写操作的模式。原因在于写操作不仅要更新数据页,还会产生WAL记录,触发后台写盘和检查点,进而影响整体吞吐。如果只使用-S测出一个很高的TPS,就认为数据库可以支撑相同数量的真实混合请求,会严重高估容量。合理的做法是分别测试只读、只写和按业务比例混合的脚本,并观察不同阶段的延迟分布。
结果解读与常见测试误区
pgbench输出中有两个TPS值,一个是不含初始连接时间的tps,另一个是including initial connection time。短时测试中初始连接时间对结果影响不大,但如果测试时长只有10秒,统计波动会非常明显。一般建议单轮测试至少持续120秒,并且正式记录前先执行一轮30秒左右的预热,避免缓存未填充导致数据失真。使用-r参数可以查看每条SQL语句的平均延迟,这是定位热点语句的重要依据。
常见误区还包括过度关注TPS峰值,忽视高并发下的延迟上升和错误率。pgbench默认不会自动重试失败事务,如果日志中出现大量could not serialize access或deadlock detected,说明并发已经超出当前配置的合理范围。此时应该调整测试参数或数据库配置,而不是把失败事务当成成功事务计算。另一个问题是忽略检查点和autovacuum的干扰,测试期间如果恰好触发大检查点,TPS会出现明显跌谷,直接取平均值会低估性能。可以通过设置checkpoint_timeout、max_wal_size等参数,或者多轮测试取中位数来降低影响。
最终结论是:pgbench测试的核心不是追求最高TPS,而是让参数组合尽可能接近真实业务负载。并发数、线程数、读写比例、是否使用连接池、是否使用预备语句,这些因素共同决定了测试结果的说服力。只有把参数设计清楚,并在相同条件下重复多次,才能得到可比较、可复现的数据库性能基线。
pgbench参数调优PostgreSQL性能测试修改时间:2026-08-21 01:55:57