由 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%。