PostgreSQL提供了几个事务快照函数,实测发现,通过这些函数取到的事务快照可能要比自己预想的要有一个延迟。
1. 功能说明
几个
事务快照函数的功能说明参考PostgreSQL手册。
-----------------------------------------------------------------------------------------------------------
显示的函数在一个输出形式中提供服务器事务信息。 这些函数的主要用途是为了确定在两个快照之间有哪个事务提交。
Table 9-56. 事务ID和快照
名字
|
返回类型
|
描述
|
txid_current()
|
bigint
|
获取当前事务 ID
|
txid_current_snapshot()
|
txid_snapshot
|
获取当前快照
|
txid_snapshot_xip(txid_snapshot)
|
setof bigint
|
获取在快照中进行中的事务ID
|
txid_snapshot_xmax(txid_snapshot)
|
bigint
|
获取快照的 xmax
|
txid_snapshot_xmin(txid_snapshot)
|
bigint
|
获取快照的xmin
|
txid_visible_in_snapshot(bigint, txid_snapshot)
|
boolean
|
在快照中事务ID是否可见?(不使用子事务ID)
|
内部事务 ID 类型(xid)是32位,每40亿事务循环。然而这些函数导出一个64位格式, 是使用一个"epoch"计数器扩展,所以在安装过程中不会循环。 这些函数使用的数据类型txid_snapshot,存储在某时刻事物ID可见性的信息。 其组件描述在。
Table 9-57. 快照组件
名字
|
描述
|
xmin
|
最早的事务ID(txid)仍然活动。所有较早事务将是可见提交了,或者要么死掉回滚了。
|
xmax
|
首先作为尚未分配的txid。所有大于或等于此的txids作为这时的快照都是尚未开始的,因此不可见。
|
xip_list
|
在当前快照活动的txids。这个列表只包含在xmin和xmax 之间的活动txids;有可能活动的txids高于xmax。 一个xmin <= txid < xmax,并且不在这个列表中的txid, 是在快照的这个时间已经完成的,因此要么可见或死掉对应它的提交状态。 这个列表不包含子事务的txids。
|
txid_snapshot的文本表示为:xmin:xmax:xip_list。 例如10:20:10,14,15意思为:xmin=10, xmax=20, xip_list=10, 14, 15。
-----------------------------------------------------------------------------------------------------------
2. 实际测试
通过在PostgreSQL9.3上实际测试,进一步了解了一些细节。
1)单独调用txid_current_snapshot()函数,不会产生新的事务
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928129:3928129:
-
(1 row)
-
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928129:3928129:
-
(1 row)
3928129是尚未分配的事务ID,两次执行,它的值没有变化。
2)单独调用txid_current()函数,会产生新的事务
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928129
-
(1 row)
-
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928130
-
(1 row)
-
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928131:3928131:
-
(1 row)
3)对不影响数据库状态的查询不会产生新事务ID。
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928131:3928131:
-
(1 row)
-
-
postgres=# select * from msg limit 1;
-
id | msg
-
--------+--------
-
182002 | 182002
-
(1 row)
-
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928131:3928131:
-
(1 row)
4)事务快照可能存在滞后
先在session1开一个事务。
session1:
-
postgres=# begin;
-
BEGIN
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928131
-
(1 row)
但是在session2中却看不到这个事务(事务快照的xmax没有变化)
session2:
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928131:3928131:
-
(1 row)
session1提交后,session2的快照得到更新。
session1:
-
postgres=# commit;
-
COMMIT
session2:
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928132:3928132:
-
(1 row)
5)事务快照可能不包含自身事务
把4)引申一下,就会发现事务快照对当前事务也可能存在滞后,即事务快照不包含自身事务。自身事务从快照看就是一个“未来的”事务。
-
postgres=# begin;
-
BEGIN
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928132:3928132:
-
(1 row)
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928132
-
(1 row)
-
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928132:3928132:
-
(1 row)
6)任何一个创建了新事务ID的事务结束时,所有会话的事务快照得到更新
在session1开一个事务,但在session2的事务快照中看不到这个事务。
session1:
-
postgres=# begin;
-
BEGIN
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928142
-
(1 row)
session2:
-
postgres=# begin;
-
BEGIN
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928143
-
(1 row)
-
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-----------------------
-
3928142:3928142:
-
(1 row)
在另一个会话session3中任意提交或回滚一个需要创建新事务ID的事务,session2就可以看到session1的事务了。
session3:
-
postgres=# select txid_current();
-
txid_current
-
--------------
-
3928144
-
(1 row)
session2:
-
postgres=# select txid_current_snapshot();
-
txid_current_snapshot
-
-------------------------
-
3928142:3928145:3928142
-
(1 row)
需要注意的是:即使这样,
session2的事务快照中记录的活动事务中也没有包含自身事务(
3928143)。是不是
事务快照中的活动事务一定不包含自身事务?这个手册没有说。
3.总结
PostgreSQL可能出于性能的考虑,延迟了快照的更新。每次事务结束时更新一次全局的事务快照,而不是在事务开始时更新快照。但是这个延迟不影响事务的可见性判断。如果一个活动的事务没有出现在事务快照中,表示自这个事务创建后,还没有发生任何事务提交,也就是事务快照保存的还是这个事务开始前的状态。进一步,这个活动的事务ID必然大于等于事务快照的xmax属性,对快照来说这是一个"未来的"事务,是不可见的,这与活动事务的可见性结果一致。
简而言之,PostgreSQL中的事务快照是上一次系统发生事务提交或回滚时的事务快照,而不是获取事务快照时的。
阅读(4619) | 评论(0) | 转发(1) |