事务快照(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
列表。该列表仅包括xmin
与xmax
之间的txid
。 例如,在快照100:104:100,102
中,xmin
是100
,xmax
是104
,而xip_list
为100,102
。以下显示了两个具体的示例:
图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 READ
或SERIALIZABLE
隔离级别),事务只会在执行第一条SQL命令时获取一次快照。获取的事务快照用于元组的可见性检查,如 5.7 可见性检查 所述。
使用获取的快照进行可见性检查时,所有活跃 的事务都必须被当成IN PROGRESS
的事务等同对待,无论它们实际上是否已经提交或中止。这条规则非常重要,因为它正是READ COMMITTED
和REPEATABLE READ/SERIALIZABLE
隔离级别中表现差异的根本来源,我们将在接下来几节中频繁回到这条规则上来。
在本节的剩余部分中,我们会通过一个具体的场景来描述事务与事务管理器,如图5.9所示。
图5.9 事务管理器与事务
事务管理器始终保存着当前运行的事务的有关信息。假设三个事务一个接一个地开始,并且Transaction_A
和Transaction_B
的隔离级别是READ COMMITTED
,Transaction_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_A
和Transaction_B
。 - T4:
Transaction_A
已提交。事务管理器删除有关此事务的信息。 - T5:
Transaction_B
和Transaction_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
不可见。
下一节:可见性检查规则是一组规则,用于确定一条元组是否对一个事务可见,可见性检查规则会用到元组的t_xmin和t_xmax,提交日志clog,以及已获取的事务快照。这些规则太复杂,无法详细解释,故本书只列出了理解后续内容所需的最小规则子集。在下文中省略了与子事务相关的规则,并忽略了关于t_ctid的讨论,比如我们不会考虑在同一个事务中对一条元组多次重复更新的情况。