Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2951463
  • 博文数量: 199
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 4126
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-06 19:06
个人简介

半个PostgreSQL DBA,热衷于数据库相关的技术。我的ppt分享https://pan.baidu.com/s/1eRQsdAa https://github.com/chenhuajun https://chenhuajun.github.io

文章分类

全部博文(199)

文章存档

2020年(5)

2019年(1)

2018年(12)

2017年(23)

2016年(43)

2015年(51)

2014年(27)

2013年(21)

2011年(1)

2010年(4)

2009年(5)

2008年(6)

分类: Mysql/postgreSQL

2015-03-06 13:30:42

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()函数,不会产生新的事务

  1. postgres=# select txid_current_snapshot();
  2.  txid_current_snapshot
  3. -----------------------
  4.  3928129:3928129:
  5. (1 row)

  6. postgres=# select txid_current_snapshot();
  7.  txid_current_snapshot
  8. -----------------------
  9.  3928129:3928129:
  10. (1 row)
3928129是尚未分配的事务ID,两次执行,它的值没有变化。

2)单独调用txid_current()函数,会产生新的事务

  1. postgres=# select txid_current();
  2.  txid_current
  3. --------------
  4.       3928129
  5. (1 row)

  6. postgres=# select txid_current();
  7.  txid_current
  8. --------------
  9.       3928130
  10. (1 row)

  11. postgres=# select txid_current_snapshot();
  12.  txid_current_snapshot
  13. -----------------------
  14.  3928131:3928131:
  15. (1 row)

3)对不影响数据库状态的查询不会产生新事务ID。

  1. postgres=# select txid_current_snapshot();
  2.  txid_current_snapshot
  3. -----------------------
  4.  3928131:3928131:
  5. (1 row)

  6. postgres=# select * from msg limit 1;
  7.    id | msg
  8. --------+--------
  9.  182002 | 182002
  10. (1 row)

  11. postgres=# select txid_current_snapshot();
  12.  txid_current_snapshot
  13. -----------------------
  14.  3928131:3928131:
  15. (1 row)

4)事务快照可能存在滞后
先在session1开一个事务。
session1:
  1. postgres=# begin;
  2. BEGIN
  3. postgres=# select txid_current();
  4.  txid_current
  5. --------------
  6.       3928131
  7. (1 row)

但是在session2中却看不到这个事务(事务快照的xmax没有变化)
session2:
  1. postgres=# select txid_current_snapshot();
  2.  txid_current_snapshot
  3. -----------------------
  4.  3928131:3928131:
  5. (1 row)

session1提交后,session2的快照得到更新。
session1:
  1. postgres=# commit;
  2. COMMIT

session2:
  1. postgres=# select txid_current_snapshot();
  2.  txid_current_snapshot
  3. -----------------------
  4.  3928132:3928132:
  5. (1 row)

5)事务快照可能不包含自身事务
把4)引申一下,就会发现事务快照对当前事务也可能存在滞后,即事务快照不包含自身事务。自身事务从快照看就是一个“未来的”事务。

  1. postgres=# begin;
  2. BEGIN
  3. postgres=# select txid_current_snapshot();
  4.  txid_current_snapshot
  5. -----------------------
  6.  3928132:3928132:
  7. (1 row)
  8. postgres=# select txid_current();
  9.  txid_current
  10. --------------
  11.       3928132
  12. (1 row)

  13. postgres=# select txid_current_snapshot();
  14.  txid_current_snapshot
  15. -----------------------
  16.  3928132:3928132:
  17. (1 row)

6)任何一个创建了新事务ID的事务结束时,所有会话的事务快照得到更新
在session1开一个事务,但在session2的事务快照中看不到这个事务。
session1:
  1. postgres=# begin;
  2. BEGIN
  3. postgres=# select txid_current();
  4.  txid_current
  5. --------------
  6.      3928142
  7. (1 row)

session2:
  1. postgres=# begin;
  2. BEGIN
  3. postgres=# select txid_current();
  4.  txid_current
  5. --------------
  6.       3928143
  7. (1 row)

  8. postgres=# select txid_current_snapshot();
  9.  txid_current_snapshot
  10. -----------------------
  11.  3928142:3928142:
  12. (1 row)

在另一个会话session3中任意提交或回滚一个需要创建新事务ID的事务,session2就可以看到session1的事务了。
session3:
  1. postgres=# select txid_current();
  2.  txid_current
  3. --------------
  4.      3928144
  5. (1 row)

session2:
  1. postgres=# select txid_current_snapshot();
  2.   txid_current_snapshot
  3. -------------------------
  4.  3928142:3928145:3928142
  5. (1 row)
需要注意的是:即使这样,session2的事务快照中记录的活动事务中也没有包含自身事务(3928143)。是不是事务快照中的活动事务一定不包含自身事务?这个手册没有说。

3.总结

PostgreSQL可能出于性能的考虑,延迟了快照的更新。每次事务结束时更新一次全局的事务快照,而不是在事务开始时更新快照。但是这个延迟不影响事务的可见性判断。如果一个活动的事务没有出现在事务快照中,表示自这个事务创建后,还没有发生任何事务提交,也就是事务快照保存的还是这个事务开始前的状态。进一步,这个活动的事务ID必然大于等于事务快照的xmax属性,对快照来说这是一个"未来的"事务,是不可见的,这与活动事务的可见性结果一致。
简而言之,PostgreSQL中的事务快照是上一次系统发生事务提交或回滚时的事务快照,而不是获取事务快照时的。








阅读(4629) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~