使用 pgbench 测试 PostgreSQL 表膨胀

John Doe 五月 21, 2026

摘要:在本文中,我们将了解如何在 PostgreSQL 中使用 pgbench 进行表膨胀测试。

目录

为什么需要关注表膨胀?

众所周知,在 PostgreSQL 中有 autovacuum 后台进程定期进行垃圾清理,但它是 PostgreSQL 防止表膨胀的第一道防线,并不是万能的。表膨胀的根本原因是 MVCC 机制与业务写入模式的矛盾,而 autovacuum 的局限性(资源限制、事务可见性限制、不做物理收缩)决定了它无法完全杜绝膨胀。

autovacuum 只是一个 “后台清洁工”,它的设计目标是在不显著影响业务性能的前提下渐进式清理垃圾,而非彻底杜绝膨胀。当 autovacuum 的清理速度跟不上垃圾产生速度,或因各种限制无法清理某些垃圾时,表膨胀就会发生。

autovacuum 的默认配置是为通用场景设计的,对于频繁更新或删除的业务系统,默认配置往往过于保守。而且数据库中的各个表,业务模型可能各不一样。autovacuum 的配置可能会出现:

  • 触发阈值过高:默认 autovacuum_vacuum_scale_factor=0.2,即当死亡元组占表大小的 20% 时才触发 autovacuum。对于大表(如 100GB),这意味着需要产生 20GB 的死亡元组才会触发清理,此时表已经严重膨胀。
  • 清理速度过慢:默认 autovacuum_vacuum_cost_limit=200,为了不影响前台业务,autovacuum 会严格控制自身的 IO 消耗。对于高更新业务,清理速度会远跟不上垃圾产生速度。
  • 工作进程数量不足:默认 autovacuum_max_workers=3,当数据库中有大量表需要清理时,工作进程会排队,导致部分表长时间得不到清理。

准备基础数据

调整数据库配置参数(修改配置文件postgresql.conf):

shared_buffers = 1GB
synchronous_commit = off

注意:我们在这里将参数 synchronous_commit 设置为 off,用于模拟高速存储设备支持的业务负载。不建议在实际生产环境上这样做。

新建数据库(例如benchdb):

CREATE DATABASE benchdb;

创建 pgbench 默认测试数据表:

pgbench -i --scale=100 benchdb

注意:缩放因子--scale=100约生成 1000 万条数据,可按需调整数值。

修改表的存储参数,禁用 autovacuum:

ALTER TABLE pgbench_accounts SET (autovacuum_enabled = off);

查看表和索引占用的空间大小:

SELECT pg_size_pretty(pg_relation_size('pgbench_accounts'));
 pg_size_pretty
----------------
 1281 MB
(1 row)

SELECT pg_size_pretty(pg_relation_size('pgbench_accounts_pkey'));
 pg_size_pretty
----------------
 214 MB
(1 row)

编写自定义测试脚本

为了能够对比分析数据库的膨胀情况,我们选择采用 “删除 + 插入” 的测试模型,而非 “频繁更新” 的测试模型。因为带有回滚段的数据库都支持原位更新,相对于 PostgreSQL 的追加式更新,具有明显的差异。

想要执行 pgbench 删除 + 插入测试,需要借助自定义 SQL 脚本模拟高写入量或高频数据更迭场景,因为默认的 TPC-B 基准测试负载更偏向数据更新操作。

新建test.sql文件,定义删除与插入混合业务负载:

\set aid random(1, 100000 * :scale) / 8 * 8 + :client_id
\set bid random(1, 1 * :scale)
\set delta random(-5000, 5000)

DELETE FROM pgbench_accounts WHERE aid = :aid;

INSERT INTO pgbench_accounts (aid, bid, abalance, filler)
  VALUES (:aid, :bid, :delta, 'new row');

执行基准压力测试

通过pgbench搭配-f参数指定自定义脚本运行测试:

pgbench --no-vacuum -f test.sql --time=600 --client=8 --jobs=8 benchdb
pgbench (18.4)
transaction type: test.sql
scaling factor: 1
query mode: simple
number of clients: 8
number of threads: 8
maximum number of tries: 1
duration: 600 s
number of transactions actually processed: 16678556
number of failed transactions: 0 (0.000%)
latency average = 0.288 ms
initial connection time = 5.231 ms
tps = 27797.816364 (without initial connection time)

参数释义:

  • --no-vacuum:测试前不执行数据表清理操作
  • -f test.sql:调用自定义测试脚本
  • --time=600:测试持续运行 600 秒
  • --client=8:模拟 8 个并发客户端
  • --jobs=8:启用 8 个工作线程

查看表和索引占用的空间大小:

SELECT pg_size_pretty(pg_relation_size('pgbench_accounts'));
 pg_size_pretty
----------------
 3416 MB
(1 row)

SELECT pg_size_pretty(pg_relation_size('pgbench_accounts_pkey'));
 pg_size_pretty
----------------
 216 MB
(1 row)

测试结果汇总

在 Redrock Postgres 中进行同样的测试,收集测试运行前后的表和索引大小:

数据库 初始表大小 初始索引大小 测试后表大小 测试后索引大小 事务吞吐量 TPS
PostgreSQL 1281 MB 214 MB 3416 MB 216 MB 27798
Redrock Postgres 1132 MB 216 MB 1295 MB 216 MB 22528

通过测试过程中持续观察发现,PostgreSQL 表的占用空间会不断上升,在测试持续运行 10 分钟后,表膨胀到了初始大小的 2.67 倍。而 Redrock Postgres 表的占用空间会趋于稳定,最终表的膨胀度保持在 12.6%。