事务回卷浅析

Posted 瀚高PG实验室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事务回卷浅析相关的知识,希望对你有一定的参考价值。

作者:瀚高PG实验室(Highgo PG Lab)-Chrisx

事务回卷浅析


在这里,我们描述事务ID回卷问题。

事务ID(txid)

每当事务开始时,由事务管理器分配一个唯一标识符 事务id(txid)。PostgreSQL的txid是一个32位无符号整数,约为42亿。如果在事务开始后调用内部函数 txid_current(),则返回当前的txid,如下所示。

testdb=# BEGIN;
BEGIN
test=> SELECT txid_current();
 txid_current
--------------
          592
(1 row)

PostgreSQL保留以下三个特殊的 txid:

  • 0 表示 Invalid txid,无效txid。
  • 1 表示 Bootstrap txid, 它仅用于数据库集群的初始化。
  • 2 表示 Frozen txid, 用来描述冻结状态。

所以数据库系统的第一个正常的事务ID是从3开始的,然后不停递增,达到4字节整数的最大值后,再从3开始。事务ID为0、1、2的始终保留。

事务可比较

PostgreSQL的 MVCC 依赖于能够进行时间比较的事务 ID(XID):mvcc是基于 Timestamp Ordering(T/O)的多版本并发,PostgreSQL采用了事物 ID(XID),事物ID是严格按照时间顺序产生的,因此满足 T/O 的基本要求。

如果一个行版本的插入 XID 大于当前事务的 XID,它就是“属于未来的”并且不应该对当前事务可见。

例如,从txid 100的角度看,大于100的txid表示“将来的”,并且它们在txid 100中不可见; 小于100的txid表示"过去的"并且可见

979899100101102

事务空间

事务 ID 的尺寸是有限的(32位),只能存放约 42 亿个事务。为了能够循环使用事务空间,提供了一种机制,普通 XID 使用模-2^32算法来比较。这意味着对于每一个普通 XID都有 21 亿个 XID “更老” 并且有 21 亿个“更新”,另一种解释的方法是普通 XID 空间是没有端点的环。 因此,一旦一个行版本创建时被分配了一个特定的普通 XID,该行版本将成为接下来 21 亿个事务的“过去”(与我们谈论的具体哪个普通 XID 无关)。如果在 21 亿个事务之后该行版本仍然存在,它将突然变得好像在未来。

txid

事务回卷

事务回卷问题(transaction wraparound problem) 是指本来属于过去的事务突然间就变成了属于未来 — 这意味着它们的输出变成不可见。简而言之,就是灾难性的数据丢失(实际上数据仍然在那里,但是如果你不能得到它也无济于事)。为了避免发生这种情况,必要至少每 21 亿个事务就清理每个数据库中的每个表。

假定 txid 100 插入元组 Tuple_1,即 Tuple_1 的 普通t_xmin 为 100。服务器运行了很长时间,Tuple_1 没有被修改。当前 txid 为21亿+100,并执行 SELECT 命令。此时,Tuple_1 可见,因为 txid 100 是过去(可见的)。然后,执行相同的SELECT 命令; 此时,目前的 txid 为21亿+101。然而,Tuple_1 不再可见,因为 txid 100 在未来(如下图)。

txidwrap

防止事务回卷

周期性的清理能够解决该问题,pg数据库提供了VACUUM清理机制。VACUUM会把行标记为 冻结 FREEZE,这表示它们是被一个在足够远的过去提交的事务所插入, 这样从 MVCC 的角度来看,效果就是该插入事务对所有当前和未来事务来说当然都是可见的。PostgreSQL保留了一个特殊的 XID (FrozenTransactionId),这个 XID 并不遵循普通 XID 的比较规则 并且总是被认为比任何普通 XID 要老。要阻止事务回卷的发生,被冻结行版本会被看成其插入 XID 为FrozenTransactionId, 这样它们对所有普通事务来说都是“在过去”,而不管回卷问题。并且这样 的行版本将一直有效直到被删除,不管它有多旧。

以上是关于事务回卷浅析的主要内容,如果未能解决你的问题,请参考以下文章

片段事务中的实例化错误

BottomNavigationView 滞后于片段事务

事务浅析

浅析Redis事务

浅析Redis事务

浅析Spring事务实现原理