5.5 事务快照

事务快照(transaction snapshot)是一个数据集,存储着某个特定事务在某个特定时间点所看到的事务状态信息:哪些事务处于活跃状态。这里活跃状态意味着事务正在进行中,或还没有开始。

事务快照在PostgreSQL内部的文本表示格式定义为100:100:。举个例子,这里100:100:意味着txid < 100的事务处于非活跃状态,而txid ≥ 100的事务处于活跃状态。下文都将使用这种便利形式来表示。如果读者还不熟悉这种形式,请参阅下文。

内置函数txid_current_snapshot及其文本表示

函数txid_current_snapshot显示当前事务的快照。

testdb=# SELECT txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 100:104:100,102
(1 row)

txid_current_snapshot的文本表示是xmin:xmax:xip_list,各部分描述如下。

  • xmin 最早仍然活跃的事务的txid。所有比它更早的事务txid < xmin要么已经提交并可见,要么已经回滚并生成死元组。
  • xmax 第一个尚未分配的txid。所有txid ≥ xmax的事务在获取快照时尚未启动,因而其结果对当前事务不可见。
  • xip_list 获取快照时活跃事务txid列表。该列表仅包括xminxmax之间的txid。 例如,在快照100:104:100,102中,xmin100xmax104,而xip_list100,102

以下显示了两个具体的示例:

图5.8 事务快照的表示样例图5.8 事务快照的表示样例

第一个例子是100:100:,如图图5.8(a)所示,此快照表示:

  • 因为xmin为100,因此txid < 100的事务是非活跃的
  • 因为xmax为100,因此txid ≥ 100的事务是活跃的

第二个例子是100:104:100,102,如图5.8(b)所示,此快照表示:

  • txid < 100的事务不活跃。
  • txid ≥ 104的事务是活跃的。
  • txid等于100和102的事务是活跃的,因为它们在xip_list中,而txid等于101和103的事务不活跃。

事务快照是由事务管理器提供的。在READ COMMITTED隔离级别,事务在执行每条SQL时都会获取快照;其他情况下(REPEATABLE READSERIALIZABLE隔离级别),事务只会在执行第一条SQL命令时获取一次快照。获取的事务快照用于元组的可见性检查,如 5.7 可见性检查 所述。

使用获取的快照进行可见性检查时,所有活跃 的事务都必须被当成IN PROGRESS的事务等同对待,无论它们实际上是否已经提交或中止。这条规则非常重要,因为它正是READ COMMITTEDREPEATABLE READ/SERIALIZABLE隔离级别中表现差异的根本来源,我们将在接下来几节中频繁回到这条规则上来。

在本节的剩余部分中,我们会通过一个具体的场景来描述事务与事务管理器,如图5.9所示。

图5.9 事务管理器与事务图5.9 事务管理器与事务

事务管理器始终保存着当前运行的事务的有关信息。假设三个事务一个接一个地开始,并且Transaction_ATransaction_B的隔离级别是READ COMMITTEDTransaction_C的隔离级别是REPEATABLE READ

  • T1: Transaction_A启动并执行第一条SELECT命令。执行第一个命令时,Transaction_A请求此刻的txid和快照。在这种情况下,事务管理器分配txid=200,并返回事务快照200:200:
  • T2: Transaction_B启动并执行第一条SELECT命令。事务管理器分配txid=201,并返回事务快照200:200:,因为Transaction_A(txid=200)正在进行中。因此无法从Transaction_B中看到Transaction_A
  • T3: Transaction_C启动并执行第一条SELECT命令。事务管理器分配txid=202,并返回事务快照200:200:,因此不能从Transaction_C中看到Transaction_ATransaction_B
  • T4: Transaction_A已提交。事务管理器删除有关此事务的信息。
  • T5: Transaction_BTransaction_C执行它们各自的SELECT命令。 Transaction_B需要一个新的事务快照,因为它使用了READ COMMITTED隔离等级。在这种情况下,Transaction_B获取新快照201:201:,因为Transaction_A(txid=200)已提交。因此Transaction_A的变更对Transaction_B可见了。 Transaction_C不需要新的事务快照,因为它处于REPEATABLE READ隔离等级,并继续使用已获取的快照,即200:200:。因此,Transaction_A的变更仍然对Transaction_C不可见。