自治子事务
自治子事务
自治子事务(简称AST)表示单个会话能够运行多个独立的事务,就好像多个不同的会话分别执行每个事务一样。
行为
AST 只能在另一个事务内部发生。
在现有事务(称为 T0)内部,用户可以决定启动一个子事务。然后 T0 被暂停并压入 AST 栈,并启动一个新的事务(称为 T1)。
将来某个时刻,用户可以提交子事务;在 T1 提交后,T0 从 AST 栈中弹出并恢复。
用户也可以决定提交父事务 T0,在这种情况下,T1 会被提交,然后 T0 从 AST 栈中弹出并提交。
所有事务都同步发生;在任何时刻,只有一个事务可以处于活动状态,而栈中则有零个(或多个)暂停的事务。
T0 和 T1 的所有可能的 COMMIT/ROLLBACK 组合都可以发生;例如,可以 COMMIT T1 并 ROLLBACK T0。
可以嵌套子事务,直到达到全局资源限制(例如 AST 栈大小),该限制可以在服务器上设置。
示例 1(一个子事务)
下图描述了一个事务执行子事务的示例。实线表示活动事务,虚线表示已暂停并压入 AST 栈的事务。时间向下流动。
BEGIN (start ordinary tx T0); | INSERT INTO t VALUES (1); :\ : BEGIN SUBTRANSACTION (start AST tx T1, pushes T0 into stack); : | : INSERT INTO t VALUES (2); : | : COMMIT SUBTRANSACTION / ROLLBACK SUBTRANSACTION; (ends tx T1, pops tx T0 from stack); :/ COMMIT / ROLLBACK; (ends tx T0)
根据 COMMIT 和 ROLLBACK 之间的两个选择,我们可以从
SELECT sum(x) from t;
示例 2(多个子事务)
父事务可以有多个子事务,只需重复应用推入/弹出循环即可。
BEGIN (start ordinary tx T0); | INSERT INTO t VALUES (1); :\ : BEGIN SUBTRANSACTION (start AST tx T1, pushes T0 into stack); : | : INSERT INTO t VALUES (2); : | : COMMIT SUBTRANSACTION / ROLLBACK SUBTRANSACTION; (ends tx T1, pops tx T0 from stack); :/ | :\ : BEGIN SUBTRANSACTION (start AST tx T2, pushes T0 into stack); : | : INSERT INTO t VALUES (4); : | : COMMIT SUBTRANSACTION / ROLLBACK SUBTRANSACTION; (ends tx T2, pops tx T0 from stack); :/ COMMIT / ROLLBACK; (ends tx T0)
注意。如果我们额外了解下一步要执行的语句(例如,当 T0 在 PL/PGSQL 函数内部执行时),则可能会出现不同的语义。在这种情况下,子事务的提交会立即启动一个新的子事务,而父事务 T0 只有在函数返回时才会从栈中弹出。然后,该函数等效于一系列事务 T1、T2...,这些事务都是 T0 的子事务,无需在 T1 和 T2 之间、T2 和 T3 之间等从栈中弹出/推入 T0。
可见性
可见性规则与通过 dblink 执行的独立事务的情况相同。T1 无法看到 T0 的效果,因为 T0 尚未提交。T0 可能会看到 T1 的效果,具体取决于其自身的隔离级别。
现在,单会话死锁成为可能,因为 AST 可能与同一会话中暂停的事务之一纠缠在一起。
警告:AST 打破了在事务开始和结束之间同一会话中没有其他事务处于活动状态的假设;这在事务访问会话级资源(例如临时表的 ON COMMIT DROP/DELETE ROWS)的情况下,会增加冲突的可能性。
参考文献
自治事务至少已经讨论过两次
- http://archives.postgresql.org/pgsql-hackers/2008-01/msg00893.php
- http://archives.postgresql.org/pgsql-hackers/2011-04/msg01149.php
这里描述的方法类似于这个
以下补丁已被提出