PostgreSQL 插入性能对比分析

John Doe 十一月 5, 2025

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

目录

表数据插入场景: 用例

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

CREATE TABLE test_table(id int, info text);

\timing

INSERT INTO test_table
  SELECT n, md5(random()::text)
    FROM generate_series(1, 10000000) as n;
Time: 11973.773 ms (00:11.974)

VACUUM (verbose) test_table;
INFO:  vacuuming "postgres.public.test_table"
INFO:  finished vacuuming "postgres.public.test_table": index scans: 0
pages: 0 removed, 83334 remain, 83334 scanned (100.00% of total)
tuples: 0 removed, 10000000 remain, 0 are dead but not yet removable
...
Time: 870.670 ms

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

下面我们在 Redrock Postgres 的数据库中,创建和上面相同的表,并插入同样的数据,查看插入操作的性能。

CREATE TABLE test_table(id int, info text);

\timing

INSERT INTO test_table
  SELECT n, md5(random()::text)
    FROM generate_series(1, 10000000) as n;
Time: 15654.149 ms (00:15.654)

因为 Redrock Postgres 引入了回滚日志,插入过程不仅需要往堆表插入元组,还需要记录回滚日志,这带来了 30.7% 的性能开销,计算方式为(15.654 - 11.974) / 11.974

关于 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 会表现更加出色。比如金融交易、库存管理等关键业务系统,更新操作极其频繁的核心业务。