什么是事务年龄,事务回卷。
如果对postgresql数据库的mvcc有一定的了解应该就知道在postgresql中的表有几个隐藏字段xmin,xmax,cmin,cmax。当开启一个事务执行第一条命令后xmin就会记录一个事务号。
A事务开启这行数据是我已经插入好的可以看到xmin是10442。此时A窗口的事务未提交。
B事务再开启一个事务,查询这张表查不到这条数据,因为postgresql和mysql数据库默认的隔离级别都是读已提交,这个隔离级别下允许出现“不可重复读”,“幻读”。如果A事务尚未提交B事务就能查询,这就出现脏读了。
那么将A事务提交,就发现能查到了。
那如果A事务再将同一条记录更新后提交呢?会发现B事务查询到的数据也随之改变,这就是“不可重复读”。B事务会读取到其他事务更改后提交的数据。
根据上图我们可以确认一点xmin字段的记录的是插入数据的事务号。并且后开启的事务看不到先开启事务的操作。接下来我们看下cmin和cmax字段扮演了什么样的角色。
A事务刚才在10448事务下插入了四条数据,因为这是在同一个事务下插入的所以xmin也是10448一样的,cmin和cmax变了4次从0到3,说明cmin和cmax记录事务中记录的变更。无论是INSERT、UPDATE、DELETE都会导致这两个字段同时加一。
。
接下来我们看下xmax这个字段扮演了什么样的角色。
A事务删除一行记录,有6条数据,先删除id=6的行,然后在B事务查询。
B事务查询发现xmax字段有了变化。
因为A事务的先删除并未提交,导致B事务依然能看到这行数据。所以xmax记录下了删除这一行的事务号表示是被10478这个事务删除的。
如果A事务rollback回滚的话被删除的行xmax记录依然会被保留,rollback的操作等价commit,因此再插入新的数据相当于开启了新的事务。
上文介绍了xmin,xmax,cmin,cmax的作用,但是又多出了新的问题,事务号是哪里来的,是有限的还是无限的,有限的话又是多少,用完了怎么办?
在postgresql数据库中每开启一个事务,执行第一个sql命令时事务管理器就会分配一个事务标识符txid。执行txid_current可以查到当前的txid。
也就是说,在这个事务中所有操作的txid都是10480。
Txid(事务号)是一个4字节的32位无符号数,最大的取值范围约为0-42亿。
但是在pg数据库中0-2被保留定义了其他的作用。
0:表示无效的事务id,比如当内部函数GetCurrentTransactionIdIfAny返回的值是0时就表示没有分配事务。
1:初始化时的事务id,数据库在初始化时用的事务id。
2:被冻结的的事务id,txid为2表示这个事务比任何事务都小,对任何事务可见。
对于txid=100的事务来说小于100的事务对本身是永远可见的,大于100的事务对于本身是永远不可见的。
可是上文说了txid是个4字节无符号数,最大只有42亿。只要数据库一直在运行那么txid迟早会超过这个值,因此pg数据库就有了一个机制就是冻结事务标识(Frozen txid)。当事务的txid超过了参数的阈值,数据库就会自动运行auto vacuum freeze table将达到freeze标准的元组冻结,回收tixd复用。
解决了txid有限的问题,还有新的问题假如服用了旧的txid号,假如碰到id1=4000000000,di2=1000的情况该怎么办?数据库的源码采用了这样的公式来确认那个是旧的那个新的。
当((int32) (id1 - id2)) < 0这个式子成立时,表示id2比id1更新。
而像上面刚才说的那个情况,结果应该是3900000000转换为int32位的数时由于超过了2^31这个范围会将十六进制的第一位数变为1表示是负数小于0因此成立。
并且pg数据库还规定两个事务最老和最新的txid不能超过21亿,因为如果超过了21亿在某些特定的场景不适用上文说的那个公式。