管理事务ID的例子

管理事务ID的例子

对于Greenplum数据库来说,事务ID(XID)值是一个自增的32位(232)值。最大的无符号32位置是4,294,967,295,或者说大约40亿。当达到最大值后,XID值会重新从0开始。Greenplum数据库以两种特性来处理XID值的限制:
  • 使用模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)。
注意: Greenplum数据库只给涉及DDL或者DML操作的事务分配XID值,它们通常是唯一需要XID的事务。

简单的MVCC例子

这是一个简单例子,它展示了MVCC数据库中的概念以及如何用事务ID管理数据和事务。这个简单的MVCC数据库例子有一个单一表构成:
  • 该表是一个具有2列4行数据的简单表。
  • 合法的事务ID(XID)值从0到9,在9之后XID会重新从0开始。
  • 冻结XID是-2。这和Greenplum数据库的冻结XID不同。
  • 事务都在单一行上执行。
  • 只允许插入和更新操作执行。
  • 所有被更新的行都保留在磁盘上,不允许移除废弃的操作。

该例子只更新amount值。不对表做其他更改。

管理同时发生的事务

这个表是磁盘上没有更新过的初始表数据。该表包含两个用于事务ID的数据库列xmin(创建该行的事务)和xmax(更新该行的事务)。在这个表中,更改会按照顺序增加到表的底端。

表 1. 示例表
item amount xmin xmax
widget 100 0 null
giblet 200 1 null
sprocket 300 2 null
gizmo 400 3 null
下一个表展示了执行了对amount值的一些更新后的表数据。
  • 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数据库使用了一种有点不同的方法来决定当前的表行。

表 2. 带更新的示例表
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
这个简单的MVCC数据库用XID值来判断该表的状态。例如,所有这些独立的事务并发执行。
  • 更改sprocket的amount值为133UPDATE命令(xmin值5
  • 返回sprocket的值的SELECT命令。

UPDATE事务期间,数据库会返回sprocket的值是300,直到UPDATE事务完成。

管理XID和冻结XID

对于这个简单的例子,该数据库接近于耗尽可用的XID值。当Greenplum数据库接近于耗尽可用XID值时,Greenplum数据库会采取这些行动。
  • 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" 
为了管理事务ID以及存储在磁盘上的表数据,Greenplum数据库提供了VACUUM命令。
  • 一次VACUUM操作会空出XID值,这样一个表可能有超过10行被修改xmin值为冻结XID。
  • 一次VACUUM操作会处理磁盘上废弃的或者被删除的表行。这个数据库的VACUUM命令将XID值改为obsolete以表示废弃行。一次Greenplum数据库的VACUUM操作(不带FULL选项),会适时地以对性能和数据可用性最小的影响移除磁盘上的行。
对于该示例表,一次VACUUM操作已经在该表上执行。该命令更新了磁盘上的表数据。这个版本的VACUUM命令与Greenplum数据库的命令有点不同,但是概念是相同的。
  • 对于磁盘上不再是当前行的widget和sprocket行,这些行会被标记为obsolete
  • 对于当前版本的giblet和gizmo行,其xmin已经被改为冻结XID。

    其值仍然是当前的表值(该行的xmax值为null)。不过,该表行对于所有事务是可见的,因为在执行取模计算时,其xmin值为比所有其他XID值更老的冻结XID。

VACUUM操作之后,XID值0123可以被使用。

表 3. VACUUM后的示例表
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操作。

表 4. 带有回卷XID的示例表
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更晚出现的事务。