暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Postgresql的XMAX与行锁

白鳝的洞穴 2021-05-09
1282

前两天一个朋友发现有很多数据记录的xmax不是0,他也看过老白写的关于postgresqlmvcc的文章,里面讲到一条的元组创建的时候,xmax是0,而当这条数据记录被修改的时候,会把修改这条记录的xid写入xmax,然后创建一个新的元组来存放这条记录的新副本。当这个事务提交的时候,我们就只能看到这个新的元组的数据,而看不见老的元组了,除非我们执行一致性读。

当时我也是一愣,上了点岁数,一时间还真反应不过来。后来想了半天,才想起来,xmax的作用不仅如此。Postgresql的行锁也是和xmax这个内部字段相关的。首先我们来看一看下面的一系列实验。

首先我们插入一条记录,然后我们看到xmax的值确实是0,这和我们的认知是相同的。下面我们来修改一下这条记录。

好像我们看到的xmax都是0,那么什么情况下我们才能看到xmax不为零呢?继续下面的实验。

我们看到xmax不为0了。这是为什么呢?因为这时候,xmax里的值存储的是一个行锁。当xid为355461734的事务修改这条记录的时候,原来的元组的xmax就会写入这个xid号,表示这个元组被某个事务锁住了。而新的数据会被写入一个新的元组中。当我们回滚这个UPDATE操作的时候,新元组就死掉了。而写入了锁的xid的老元组又变得可见了。这时候,我们就看到了xmax中有值了。这时候我们通过pageinspect插件就可以看的十分清晰了。

我们这时候就可以看到和这条记录相关的三个元组了,第一个元组是INSERT产生的,第二个元组是第一次UPDATE产生的,第三个元组是第二次UPDATE时候产生的(根据b字段的值可以看出)。而从select语句来看,第二个元组是可见的。
既然xmax还要用来放行锁,那么通过xmax就无法判断元组的可见性了。那么PG是如何确定元组的可见性呢?这就要用到infomask这个隐含字段了。关于infomask和infomask2字段的详细分析今天时间关系我们就不做仔细的分析了。我们直接来看结果。

我们根据infomask的位图可以判断某个元组的情况。我们再来看一个例子。

如果我们使用了select ... for update语句锁定了这条记录,我们可以看出xmax中写入了锁定这条记录的xid,而xmax_is_lock这个位图就变成true了。通过infomask可以检测到这个锁的存在。

此时我们rollback 这条记录,那么我们看到的记录中的xmax还保留着。下面我们继续实验,为了看的更清晰,我们重建一下相关的表。

然后我们在子表里插入一条记录,我们看到这个元组的xmax上被写入了一个xid,而且从infomask来看xmax上有锁标志了。

我们再开一个新的会话,同样插入一条子表的记录,来看看会发生什么。

接下来我们看看数据块中的情况:

看红色的地方,xmax的multixact标志被设置了,而且xmax里也不是一个xid了,这个471721代表什么含义呢?

当有多个事务锁了这个元组的时候,xmax是无法表示的,这时候,需要另外一个数据结构来存储这个信息了。我们通过pg_get_multixact_member可以看到锁定这条记录的两个事务。
今天关于xmax的作用我们总结一下:
1、存储MVCC副本的最大可见xid,或者为0,这是我们最初的认知
2、当有行锁的时候,存储锁定这个元组的XID
3、当有多个事务锁住这个元组的时候,存储multixact的ID。
文章转载自白鳝的洞穴,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论