PostgreSQL 热点行更新性能测试

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(多版本并发控制) 机制的必然结果:

  1. PostgreSQL 的 UPDATE 为非原地更新:每次执行 UPDATE 都会生成一个全新的行版本,仅将旧版本标记为失效,而非直接覆盖。
  2. 在有长事务存在的情况下,更新生成的所有历史行版本既无法被后台回收,也不能被更新逻辑跳过。每次新的 UPDATE 都需要从头到尾遍历该行的完整版本链,定位到当前事务可见的最新版本后才能执行写入,单次更新成本随版本数线性增长,整体操作呈现 O (N²) 时间复杂度,最终表现为性能持续下降。

而 Redrock Postgres,因为引入了回滚日志,修改元组时可实现就地更新,更新性能始终保持稳定。