管理事务ID的例子
管理事务ID的例子
- 使用模232的算法来计算XID值,这允许Greenplum数据库重用XID值。取模计算基于XID决定事务的顺序,即一个事务是在另一个之前还是之后发生。
每一个XID值可以有最多二十亿(231)个XID值表示在它之前的事务,还有二十亿(231 -1)个XID值表示在它之后更新的事务。XID值可以被认为是一组循环没有终点的值,就像24小时的时钟一样。
使用Greenplum数据库的取模计算,只要两个XID都在彼此的231个事务范围之内,比较它们能够得到正确的结果。
- Greenplum数据库用一个冻结XID值作为当前(可见)数据行的XID。设置一行的XID为冻结XID完成了两个功能。
- 当Greenplum数据库使用取模计算比较XID时,比起任何其他XID来,冻结XID总是较小的一个、较早的一个。如果一行的XID没有被设置为冻结XID并且执行了231个新的事务,从取模计算的角度来看,该行就像是一个在未来出现的行。
- 当该行的XID被设置为冻结XID时,原来的XID可以被使用而不会出现重复的XID。这将会保持磁盘上分配了XID的数据行数低于(232)。
简单的MVCC例子
- 该表是一个具有2列4行数据的简单表。
- 合法的事务ID(XID)值从0到9,在9之后XID会重新从0开始。
- 冻结XID是-2。这和Greenplum数据库的冻结XID不同。
- 事务都在单一行上执行。
- 只允许插入和更新操作执行。
- 所有被更新的行都保留在磁盘上,不允许移除废弃的操作。
该例子只更新amount值。不对表做其他更改。
管理同时发生的事务
这个表是磁盘上没有更新过的初始表数据。该表包含两个用于事务ID的数据库列xmin(创建该行的事务)和xmax(更新该行的事务)。在这个表中,更改会按照顺序增加到表的底端。
item | amount | xmin | xmax |
---|---|---|---|
widget | 100 | 0 | null |
giblet | 200 | 1 | null |
sprocket | 300 | 2 | null |
gizmo | 400 | 3 | null |
- xid = 4: update tbl set amount=208 where item = 'widget'
- xid = 5: update tbl set amount=133 where item = 'sprocket'
- xid = 6: update tbl set amount=16 where item = 'widget'
在下一个表中,粗体项是该表的当前行。其他行是废弃行,即磁盘上不是当前数据的表数据。使用xmax值,用户可以通过选择其值为null值的行来判断哪些是当前行。Greenplum数据库使用了一种有点不同的方法来决定当前的表行。
item | amount | xmin | xmax |
---|---|---|---|
widget | 100 | 0 | 4 |
giblet | 200 | 1 | null |
sprocket | 300 | 2 | 5 |
gizmo | 400 | 3 | null |
widget | 208 | 4 | 6 |
sproket | 133 | 5 | null |
widget | 16 | 6 | null |
- 更改sprocket的amount值为133的UPDATE命令(xmin值5)
- 返回sprocket的值的SELECT命令。
在UPDATE事务期间,数据库会返回sprocket的值是300,直到UPDATE事务完成。
管理XID和冻结XID
- Greenplum数据库发出一个警告提示数据库快要耗尽XID值。
WARNING: database "database_name" must be vacuumed within number_of_transactions transactions
- 在最后一个XID被分配前,Greenplum停止接受事务以防止把一个XID分配两次,并且发出下面的消息。
FATAL: database is not accepting commands to avoid wraparound data loss in database "database_name"
- 一次VACUUM操作会空出XID值,这样一个表可能有超过10行被修改xmin值为冻结XID。
- 一次VACUUM操作会处理磁盘上废弃的或者被删除的表行。这个数据库的VACUUM命令将XID值改为obsolete以表示废弃行。一次Greenplum数据库的VACUUM操作(不带FULL选项),会适时地以对性能和数据可用性最小的影响移除磁盘上的行。
- 对于磁盘上不再是当前行的widget和sprocket行,这些行会被标记为obsolete。
- 对于当前版本的giblet和gizmo行,其xmin已经被改为冻结XID。
其值仍然是当前的表值(该行的xmax值为null)。不过,该表行对于所有事务是可见的,因为在执行取模计算时,其xmin值为比所有其他XID值更老的冻结XID。
在VACUUM操作之后,XID值0、 1、2和3可以被使用。
item | amount | xmin | xmax |
---|---|---|---|
widget | 100 | obsolete | obsolete |
giblet | 200 | -2 | null |
sprocket | 300 | obsolete | obsolete |
gizmo | 400 | -2 | null |
widget | 208 | 4 | 6 |
sproket | 133 | 5 | null |
widget | 16 | 6 | null |
当一个具有xmin值为-2的行被更新后,其xmax值照例被替换为该事务的XID,在任何访问该行的并发事务结束之后这个位于磁盘上的行会被认为是被废弃。
废弃的行可以被从磁盘删除。对于Greenplum数据库,带有FULL选项的 VACUUM命令会做更深入的处理来回收磁盘空间。
XID取模计算实例
下一个表展示了在经过了更多UPDATE事务后磁盘上的表数据。XID值已经回卷并且重新从0开始。此时并没有执行额外的VACUUM操作。
item | amount | xmin | xmax |
---|---|---|---|
widget | 100 | obsolete | obsolete |
giblet | 200 | -2 | 1 |
sprocket | 300 | obsolete | obsolete |
gizmo | 400 | -2 | 9 |
widget | 208 | 4 | 6 |
sproket | 133 | 5 | null |
widget | 16 | 6 | 7 |
widget | 222 | 7 | null |
giblet | 233 | 8 | 0 |
gizmo | 18 | 9 | null |
giblet | 88 | 0 | 1 |
giblet | 44 | 1 | null |
在执行比较XID的取模计算时,Greenplum数据库会考虑行的XID和当前可用的XID范围来判断行的XID是否已经发生了XID回卷。
对于示例表来说XID回卷已经发生。按照XID的取模计算,giblet行的XID 1是一个比widget行的XID 7更晚出现的事务,虽然XID值7大于1。
对于widget和sprocket行,XID回卷并没有发生并且XID 7是一个比XID 5更晚出现的事务。