PostgreSQL 插入性能对比分析

John Doe 十一月 5, 2025

摘要:在本文中,我们将了解 PostgreSQL 插入性能的优势,及其背后的原理。

目录

表数据插入场景: 用例

下面我们在 PostgreSQL 的数据库中,创建一个表,插入一些数据,并对表数据进行清理操作,查看插入操作的性能。

\set fillerlen 64

DROP TABLE IF EXISTS test_table;
CREATE TABLE test_table(id integer, filler text);

EXPLAIN (analyze, buffers, wal, costs false)
  INSERT INTO test_table (id, filler)
    SELECT i, repeat('x', :fillerlen)
      FROM generate_series(1, 1000000) AS s(i);
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Insert on test_table (actual time=1041.617..1041.618 rows=0.00 loops=1)
   Buffers: shared hit=1024685 dirtied=12350 written=12352, temp read=1709 written=1709
   WAL: records=1000004 fpi=4 bytes=124032964 buffers full=13628
   ->  Function Scan on generate_series s (actual time=65.216..151.278 rows=1000000.00 loops=1)
         Buffers: temp read=1709 written=1709
 Planning Time: 0.064 ms
 Execution Time: 1043.212 ms
(7 rows)

上面的 INSERT 操作更新了 1000000 条记录,修改了 12350 个页面,产生了 1000004 条 WAL 日志记录,WAL 日志总量为 118 MB。

依次指定 fillerlen 参数为 64、128、256,执行上面的测试用例,收集性能相关的数据如下:

filler 列长度 (fillerlen) 修改页面数 WAL 记录数 WAL 日志量 执行时间 (ms)
64 12350 1000004 118 MB 1043.212
128 20415 1000006 182 MB 1460.017
256 37048 1000010 304 MB 1941.979

值得注意的是,PostgreSQL 的插入操作会有额外的延迟清理成本,虽然这个成本不会出现在单次的批量插入操作过程中,但是正常业务运行过程中的后台清理维护负载,会间接降低数据库系统的事务处理性能。

下面我们在 Redrock Postgres 的数据库中,执行和上面相同的测试用例,收集性能相关的数据如下:

filler 列长度 (fillerlen) 修改页面数 WAL 记录数 WAL 日志量 执行时间 (ms)
64 15861 1015852 181 MB 1527.131
128 23955 1023941 245 MB 1881.010
256 39903 1039888 368 MB 2041.142

因为 Redrock Postgres 引入了回滚日志,插入过程不仅需要往堆表插入元组,还需要记录回滚日志,这会带来一定的性能开销。

关于 PostgreSQL 更新操作的性能分析,可参见更新 PostgreSQL 表带来的索引写放大。简单来说,PostgreSQL 的更新操作耗时,和表的索引个数成正比关系。而 Redrock Postgres 的更新操作性能能够始终保持稳定。

INSERT 后的清理成本

实际上,PostgreSQL 的 INSERT 操作完成后,后面还会有一个延迟的二次更改的过程。只是,这个过程很少被人注意到。

PostgreSQL 向表中插入数据时会创建一个新元组,元组头部会记录创建该元组的事务 ID,称为元组的 xmin。PostgreSQL 将每个事务的当前状态信息存储在提交日志 (CLOG) 中。检查 CLOG 中大量事务的状态会消耗大量资源,因此 PostgreSQL 会将事务状态信息直接缓存到元组的头部。例如,如果在执行 SELECT 语句时检测到 xmin 事务已完成,PostgreSQL 会将此信息保存到元组的所谓“提示位”中。这个过程会将表页面弄脏再写入磁盘,并需要记录 WAL 日志。

另外,对于插入完的表页面,VACUUM 过程中还需要更新页面的可见性映射表和空闲空间映射表。

也就是说,PostgreSQL 中的 INSERT 实际上将一部分的成本,转移分摊到了后面的 SELECT 和 VACUUM 操作里面了。

测试结论

这个性能测试结果很好地印证了两种存储引擎的设计哲学:PostgreSQL 在"追加式"工作负载上表现出色,体现了其存储方面的设计优势;而 Redrock Postgres 在复杂的事务处理上具有明显优势,具备企业级内核的成熟度。

对于写入密集型的业务场景,PostgreSQL 是极具竞争力的选择。比如日志记录、事件采集、时序数据写入等业务系统,还有其他写入操作极其频繁的业务。

而对于需要复杂事务处理的核心交易业务场景,Redrock Postgres 会表现更加出色。比如金融交易、库存管理等关键业务系统,更新操作极其频繁的核心业务。