二月 3, 2026
摘要:在本教程中,您将学习如何在 PostgreSQL 中检查遗弃的预备事务。
目录
未提交的预备事务
当应用程序或分布式数据库中间件使用两阶段提交(2PC)时,可能会出现该问题。未提交的预备事务即便在会话断开后,仍会留存于数据库中,导致 VACUUM 无法回收失效元组。
若要使用预备事务,需修改配置文件postgresql.conf,将max_prepared_transactions参数设置为 1 或更大的值,随后重启 PostgreSQL。
max_prepared_transactions = 10
启动一个事务,将其置为两阶段提交的预备状态。
BEGIN;
PREPARE TRANSACTION 'foobar';
在另一个独立会话中,删除数据,执行 VACUUM 清理操作。
DELETE FROM t;
VACUUM (VERBOSE) t;
INFO: 00000: vacuuming "postgres.public.t"
INFO: 00000: finished vacuuming "postgres.public.t": index scans: 0
pages: 0 removed, 1 remain, 1 scanned (100.00% of total), 0 eagerly scanned
tuples: 0 removed, 100 remain, 100 are dead but not yet removable
removable cutoff: 796, which was 2 XIDs old when operation ended
new relfrozenxid: 793, which is 1 XIDs ahead of previous value
frozen: 0 pages from table (0.00% of total) had 0 tuples frozen
visibility map: 0 pages set all-visible, 0 pages set all-frozen (0 were all-visible)
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed
avg read rate: 14.974 MB/s, avg write rate: 14.974 MB/s
buffer usage: 13 hits, 4 reads, 4 dirtied
WAL usage: 4 records, 4 full page images, 25678 bytes, 25412 full page image bytes, 0 buffers full
memory usage: dead item storage 0.02 MB accumulated across 0 resets (limit 64.00 MB each)
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
此时会出现提示消息tuples: 0 removed, 100 remain, 100 are dead but not yet removable,表明失效元组未被回收;而消息removable cutoff: 796, which was 2 XIDs old when operation ended则说明,事务号 796 是导致该问题的根源。
遗弃的预备事务和 VACUUM
在两阶段提交过程中,一个分布式事务首先会使用 PREPARE 语句进行准备,然后使用COMMIT PREPARED语句提交。
一旦 PostgreSQL 准备好了事务,事务就会一直“徘徊”,直到 PostgreSQL 提交或中止它。它甚至能够在服务器重新启动后留存下来!通常,事务不会长时间保持在预备状态,但有时候会出现异常,管理员必须手动删除已准备的事务。
VACUUM 可能会被xmin水位线阻塞,正如我们在检查阻塞 VACUUM 的复制槽教程中所述。
为两阶段提交准备的事务会阻止 VACUUM 的清理,直到它被提交或回滚。
列出遗弃的预备事务
您可以使用下面的查询,查找所有已准备的事务及其xmin值:
SELECT gid, prepared, owner, database, transaction AS xmin
FROM pg_prepared_xacts
ORDER BY age(transaction) DESC;
在识别出以后,可以使用 ROLLBACK PREPARED SQL 语句删除已准备的事务,也可以使用 COMMIT PREPARED SQL 语句提交已准备的事务,如下所示:
COMMIT PREPARED <gid_from_above>;
ROLLBACK PREPARED <gid_from_above>;