说到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用于判断同一个事务内的不同命令导致的行版本变化是否可见。




