由 John Doe 六月 18, 2026
摘要:在本文中,我们将了解长事务的存在,对 PostgreSQL 热点行更新的性能表现产生的影响。
目录
热点行更新场景
热点行是指在数据库中那些会被频繁执行修改操作的数据行。高并发场景下对热点行的更新,会在 PostgreSQL 表中产生超长的数据行版本链,影响系统性能。
在电商大促、金融高频交易等场景下,同一条记录(如账户余额、商品库存)被疯狂修改,产生出超长的数据行版本链,表和索引中会迅速堆积大量的死元组,导致文件体积急剧膨胀,影响后续的更新和查询性能。如果一个更新操作执行的时间变长,将会对业务层面产生显著负面影响。
针对这类问题,单纯提高计算机硬件配置已经无法满足这样的低延迟需求。下面,我们来构造该场景的用例进行测试分析。
准备基础数据
调整数据库配置参数(修改配置文件postgresql.conf):
shared_buffers = 1GB
max_wal_size = 4GB
synchronous_commit = off
注意:我们在这里将参数 synchronous_commit 设置为 off,用于模拟高速存储设备支持的业务负载。不建议在实际生产环境上这样做。
下面我们在 PostgreSQL 18.4 的数据库中,创建一个表,插入一些 100 行数据。
CREATE TABLE pgbench_accounts (
aid int PRIMARY KEY,
bid int,
filler char(128),
abalance int
);
INSERT INTO pgbench_accounts(aid, bid, filler, abalance)
SELECT aid, 1, '', 0
FROM generate_series(1, 100) as aid;
新建test.sql文件,自定义 SQL 脚本,定义频繁更新热点行的业务负载:
-- test.sql
UPDATE pgbench_accounts SET bid = bid + 1 WHERE aid = 100;
在准备好基础表和初始数据后,让我们启动一个会话,在该会话中开始一个事务,并保持在空闲状态:
BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT filler FROM pgbench_accounts WHERE aid = 1;
下面,我们来测试启动和不启动上面的空闲事务两种场景下,频繁更新热点行的性能表现。
执行基准压力测试
通过pgbench搭配-f参数指定自定义脚本运行测试:
pgbench --no-vacuum -f test.sql --time=600 --client=8 --jobs=8
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: 300144
number of failed transactions: 0 (0.000%)
latency average = 15.993 ms
initial connection time = 5.147 ms
tps = 500.216495 (without initial connection time)
最后,我们结束上面的空闲事务。
COMMIT;
测试结果对比
汇总对比上面的测试结果,如下:
| 测试场景 | PostgreSQL | Redrock Postgres | 提升率 |
|---|---|---|---|
| 有长事务 | 500.216 | 21429.916 | 4184% |
| 无长事务 | 14519.995 | 22835.284 | 57.3% |
长事务的存在,对 PostgreSQL 热点行更新的性能表现影响极大。而对于 Redrock Postgres,基本上都能够保持稳定的性能表现。
结论
在 PostgreSQL 中,对同一行数据频繁执行 UPDATE 操作时,在有长事务存在的情况下,性能会随更新次数增加持续衰减。
该现象是 PostgreSQL MVCC(多版本并发控制) 机制的必然结果:
- PostgreSQL 的 UPDATE 为非原地更新:每次执行 UPDATE 都会生成一个全新的行版本,仅将旧版本标记为失效,而非直接覆盖。
- 在有长事务存在的情况下,更新生成的所有历史行版本既无法被后台回收,也不能被更新逻辑跳过。每次新的 UPDATE 都需要从头到尾遍历该行的完整版本链,定位到当前事务可见的最新版本后才能执行写入,单次更新成本随版本数线性增长,整体操作呈现 O (N²) 时间复杂度,最终表现为性能持续下降。
而 Redrock Postgres,因为引入了回滚日志,修改元组时可实现就地更新,更新性能始终保持稳定。