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

pg的隐藏列

原创 刘继超 2022-06-09
2061

说到pg的隐藏列需要说一下pg的mvcc
pg中的MVCC(Multi-Version Concurrency Control)利用的是tuple(元组,简单理解为表中的一行)级别的数据进行控制。对于每一个tuple,都有一个header来存储这个tuple与事务等相关的信息
言归正传
1、查询pg_attribute表,查看添加到表中的系统列以及两个列id和name:

postgres=# SELECT attname, format_type (atttypid,atttypmod) FROM pg_attribute 
WHERE attrelid = 'foo.bar'::regclass::oid ORDER BY attnum; 
attname | format_type 
----------+---------------------- 
tableoid | oid 
cmax | cid 
xmax | xidCluster Management Techniques 
cmin | cid 
xmin | xid 
ctid | tid 
id | integer 
name | character varying(5) 
(8 rows) 

ctid来查询每个元组的位置·
通过显式查询xmin,我们可以通过查找每个记录的xmin值来查看插入记录的事务ID。注意以下日志中所有记录的xmin值:
我们还可以通过显式地选择每条记录来找到它的xmax。如果xmax为0,说明它从未被删除,并且是可见的
cmin和cmax介绍
cmin:插入事务中的命令标识符(从0开始)。
cmax:删除事务中的命令标识符(从0开始)。
cmin和cmax都是表示tuple的command id,即cmin是产生该条tuple的command id,cmax是删除该tuple的command id。
cmin表示插入数据的command id,cmax表示删除数据的。那怎么通过一个字段就能识别是插入还是删除呢?想要解决这个问题,我们需要了解下combo cid。
当我们的事务中只是插入数据时,t_cid存储的就是cmin,因为此时也只有cmin是有效的。而当进行了update或者delete操作时,才会产生cmax。当这种既有cmin又有 cmax的情况,即在同一个事务中既有插入又有更新的时候,t_cid存储的就是combo cid,当事务中既有插入又有更新的时候,t_cid存储的便是combo cid。

测试一下
新插入一行时,将新插入行的xmin填写为当前的事务ID,xmax填0

db_sjz=#  select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  645 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |    0 |  648 | 11
(10 rows)

db_sjz=# insert into t10 values(12);
INSERT 0 1
db_sjz=#  select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  645 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |    0 |  648 | 11
    16418 |    0 |    0 |    0 |  657 | 12
(11 rows)

删除一行时,把被删除行上的xmax填为当前的事务ID。

db_sjz=# 
db_sjz=# select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  658 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |  659 |  648 | 11
(10 rows)

db_sjz=# delete from t10 where id=11;
DELETE 1
db_sjz=# 


在另外的session 去查看

ROLLBACK
db_sjz=# select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  658 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |  660 |  648 | 11
(10 rows)

db_sjz=# 

修改某行时,实际上操作是插入一行,旧行上的xmin不变,旧行上的xmax改为当前的事务ID,新行上的xmin填为当前事务ID,新行上的xmax填为0。

db_sjz=# select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  658 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |    0 |  648 | 11
(10 rows)

db_sjz=# begin;
BEGIN
db_sjz=#  update  t10 set id=16 where id=11;
UPDATE 1
db_sjz=# select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  658 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |    0 |  659 | 16
(10 rows)

db_sjz=# abort;
ROLLBACK
db_sjz=# select tableoid,cmax,cmin,xmax,xmin,id  from t10;
 tableoid | cmax | cmin | xmax | xmin | id 
----------+------+------+------+------+----
    16418 |    0 |    0 |  645 |  505 |  2
    16418 |    0 |    0 |  645 |  505 |  3
    16418 |    0 |    0 |  645 |  505 |  4
    16418 |    0 |    0 |  645 |  505 |  5
    16418 |    0 |    0 |  645 |  505 |  6
    16418 |    0 |    0 |  645 |  505 |  7
    16418 |    0 |    0 |  645 |  505 |  8
    16418 |    0 |    0 |  658 |  505 |  9
    16418 |    1 |    1 |  656 |  505 | 10
    16418 |    0 |    0 |  659 |  648 | 11
(10 rows)

总结一下
由以上可知,xmin就是标记插入数据行的事务ID,而xmax就是标记删除数据行的事务ID。
cmin和cmax用于判断同一个事务内的不同命令导致的行版本变化是否可见。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论