PostgreSQL 删除性能对比分析

John Doe 五月 18, 2026

摘要:在本文中,我们将了解 PostgreSQL 删除性能的表现,及其背后的原理。

目录

表数据删除场景: 用例

为了能够更清楚地观察 DELETE 操作的 WAL 日志开销,我们先将参数full_page_writes设置为 off。

SHOW server_version;
 server_version
----------------
 18.4
(1 row)

SHOW full_page_writes;
 full_page_writes
------------------
 off
(1 row)

下面我们在 PostgreSQL 的数据库中,创建一个表,插入一些数据,并对表数据进行清理操作,查看该表占用的空间大小。

CREATE TABLE t_large (id integer, data text);
INSERT INTO t_large (id, data)
  SELECT i, repeat('Pg', 512)
    FROM generate_series(1, 1000000) AS s(i);

VACUUM ANALYZE t_large;
CHECKPOINT;

SELECT pg_size_pretty(pg_table_size('t_large'));
 pg_size_pretty
----------------
 1116 MB
(1 row)

上面我们进行了检查点操作,刷写了缓冲区中的所有脏页面。这样,就可以通过 EXPLAIN ANALYZE 来查看修改的页面情况:

EXPLAIN (analyze, buffers, wal, costs false)
  DELETE FROM t_large;
                                   QUERY PLAN
--------------------------------------------------------------------------------
 Delete on t_large (actual time=1157.257..1157.258 rows=0.00 loops=1)
   Buffers: shared hit=1192561 read=93155 dirtied=142863 written=11970
   I/O Timings: shared read=9.894 write=24.343
   WAL: records=1000000 bytes=54000000 buffers full=26
   ->  Seq Scan on t_large (actual time=0.210..674.204 rows=1000000.00 loops=1)
         Buffers: shared hit=49703 read=93155 written=11970
         I/O Timings: shared read=9.894 write=24.343
 Planning:
   Buffers: shared hit=5
 Planning Time: 0.073 ms
 Execution Time: 1157.416 ms
(11 rows)

上面的 DELETE 操作删除了 1000000 条记录,修改了 142863 个页面,产生了 1000000 条 WAL 日志记录,WAL 日志总量为 51 MB。

等待大约 1 分钟之后(参数autovacuum_naptime默认值为 1 分钟),查看该表占用的空间大小:

SELECT pg_size_pretty(pg_table_size('t_large'));
 pg_size_pretty
----------------
 827 MB
(1 row)

在进行全表删除后,PostgreSQL 只回收了一部分删除的元组空间。

Redrock Postgres 的表现

下面我们在 Redrock Postgres 的数据库中,执行和上面相同的测试用例。在执行删除操作之前,查看下当前数据库中回滚段的使用情况:

SELECT oid, relname, pg_size_pretty(pg_relation_size(oid)) AS undsize
  FROM pg_class WHERE relkind = 'u'
  ORDER BY pg_relation_size(oid) DESC LIMIT 4;
 oid  |  relname  | undsize
------+-----------+---------
 8102 | pg_undo_2 | 41 MB
 8103 | pg_undo_3 | 1040 kB
 8104 | pg_undo_4 | 1040 kB
 8105 | pg_undo_5 | 1040 kB
(4 rows)

输出结果显示,前面的 INSERT 操作产生了 41 MB 的回滚日志。接下来,执行相同的删除操作:

EXPLAIN (analyze, buffers, wal, costs false)
  DELETE FROM t_large;
                                   QUERY PLAN
--------------------------------------------------------------------------------
 Delete on t_large (actual time=1368.790..1368.791 rows=0.00 loops=1)
   Buffers: shared hit=2054654 read=110057 dirtied=150071 written=26645
   I/O Timings: shared read=5.627 write=100.487
   WAL: records=1007204 bytes=125592003 buffers full=9792
   ->  Seq Scan on t_large (actual time=0.251..489.609 rows=1000000.00 loops=1)
         Buffers: shared hit=32931 read=109927 written=17821
         I/O Timings: shared read=4.972 write=32.084
 Planning:
   Buffers: shared hit=2
 Planning Time: 0.106 ms
 Execution Time: 1369.009 ms
(11 rows)

上面的 DELETE 操作删除了 1000000 条记录,修改了 150071 个页面,产生了 1007204 条 WAL 日志记录,WAL 日志总量为 120 MB。我们再次查看下回滚段的使用情况:

SELECT oid, relname, pg_size_pretty(pg_relation_size(oid)) AS undsize
  FROM pg_class WHERE relkind = 'u'
  ORDER BY pg_relation_size(oid) DESC LIMIT 4;
 oid  |  relname   | undsize
------+------------+---------
 8117 | pg_undo_17 | 56 MB
 8102 | pg_undo_2  | 41 MB
 8104 | pg_undo_4  | 1040 kB
 8105 | pg_undo_5  | 1040 kB
(4 rows)

输出结果显示,前面的 DELETE 操作产生了 56 MB 的回滚日志。

等待大约 1 分钟之后,查看该表占用的空间大小:

SELECT pg_size_pretty(pg_table_size('t_large'));
 pg_size_pretty
----------------
 24 kB
(1 row)

在进行全表删除后,Redrock Postgres 能够完全回收删除的元组空间。

DELETE 后的清理成本

实际上,PostgreSQL 的 DELETE 操作完成后,后面还会有一个延迟的二次更改的过程。

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

另外,对于删除过的元组,需要等待全表和索引执行过 VACUUM 操作后,才能完全重用删除的元组空间。

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

测试结论

PostgreSQL 的 DELETE 性能优势是有条件的,它将一部分成本转移到了后续的 SELECT 和 VACUUM 操作中。在实际生产环境中:

  • 如果您的应用偏向 “插入 + 删除” 或者 “插入 + 查询” 这两类场景,PostgreSQL 的表现会更好;
  • 如果您的应用是混合型业务场景,更新删除操作频繁且有大量复杂分析查询,Redrock Postgres 的表现会更加稳定。