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