子事务(Subtransactions)在主事务内部开启,允许将巨型事务切分为更小的逻辑单元。子事务可以在不影响外层父事务的前提下独立提交或回滚,使父事务能够继续执行。这极大简化了业务错误处理逻辑,是应用程序开发中极为常见的设计模式。子事务通常简称为 subxact。
子事务可以通过 SAVEPOINT 命令显式开启,也可以通过其他语法结构隐式触发,例如 PL/pgSQL 的 EXCEPTION 异常处理块。PL/Python 与 PL/Tcl 同样支持显式子事务。子事务内部还可以进一步嵌套嵌套子事务。顶层事务与其所有子事务构成一棵树状层级结构,因此最外层的主事务被称为顶层事务。
若子事务被分配了物理事务 ID,其 ID 被称为 “subxid”。只读子事务不会分配 subxid,仅在尝试执行写操作时才正式分配。这一操作还会联动触发该子事务的所有父级祖先事务(直至顶层事务)被分配物理事务 ID。内核严格确保父级 xid 必定小于其任意子级 subxid。
每个 subxid 的直接父级 xid 登记在 pg_subtrans 存储目录中。顶层 xid 由于没有父级故不在此记录,只读子事务亦不产生记录。
当子事务提交时,其所有已提交的带有 subxid 的子事务均被视作在该事务中子提交(subcommitted)。当子事务发生中止回滚时,其所有下属子事务均被视作中止。
当顶层事务最终提交时,其所有已子提交的子级事务会被作为已提交状态持久固化在 pg_xact 子目录中。若顶层事务最终中止,其包含的所有子事务一律全部中止,无论它们此前是否已在局部子提交。
每个事务保持打开(未回滚且未释放)的子事务越多,事务管理的系统开销就越大。每个后台进程在共享内存中最多缓存 64 个打开的 subxid;超过此阈值后,由于需要频繁跨磁盘在 pg_subtrans 中检索 subxid 记录,存储 I/O 开销将呈指数级激增。