oid
一行的对象标识符(对象ID)。
该列只有在表使用WITH OIDS创建时或者default_with_oids配置变量被设置时才存在。
该列的类型为oid(与列名一致),该类型详见第 8.18 节。
tableoid
包含这一行的表的OID。
该列是特别为从继承层次(见第 5.9 节)中选择的查询而准备,因为如果没有它将很难知道一行来自于哪个表。
tableoid可以与pg_class的oid列进行连接来获得表的名称。
xmin
插入该行版本的事务身份(事务ID)。
一个行版本是一个行的一个特别版本,对一个逻辑行的每一次更新都将创建一个新的行版本。
cmin
插入事务中的命令标识符(从0开始)。
xmax
删除事务的身份(事务ID),对于未删除的行版本为0。
对于一个可见的行版本,该列值也可能为非零。
这通常表示删除事务还没有提交,或者一个删除尝试被回滚。
cmax
删除事务中的命令标识符,或者为0。
ctid
行版本在其表中的物理位置。
注意尽管ctid可以被用来非常快速地定位行版本,但是一个行的ctid会在被更新或者被VACUUM FULL移动时改变。
因此,ctid不能作为一个长期行标识符。OID或者最好是一个用户定义的序列号才应该被用来标识逻辑行。
初始化数据
postgres=# create table tmp_t0(c0 varchar(100),c1 varchar(100));
CREATE TABLE
postgres=# insert into tmp_t0(c0,c1)values('1','1');
INSERT 0 1
postgres=# select tableoid,cmax,xmax,cmin,xmin,ctid,c0,c1,txid_current() from tmp_t0;
tableoid | cmax | xmax | cmin | xmin | ctid | c0 | c1 | txid_current
----------+------+------+------+------+-------+----+----+--------------
16393 | 0 | 0 | 0 | 1947 | (0,1) | 1 | 1 | 1948
(1 row)
postgres=# insert into tmp_t0(c0,c1)values('2','2');
INSERT 0 1
postgres=# select tableoid,cmax,xmax,cmin,xmin,ctid,c0,c1,txid_current() from tmp_t0;
tableoid | cmax | xmax | cmin | xmin | ctid | c0 | c1 | txid_current
----------+------+------+------+------+-------+----+----+--------------
16393 | 0 | 0 | 0 | 1947 | (0,1) | 1 | 1 | 1950
16393 | 0 | 0 | 0 | 1949 | (0,2) | 2 | 2 | 1950
(2 rows)
postgres=#
更新
session 1 操作
postgres=# begin;
BEGIN
postgres=# select tableoid,cmax,xmax,cmin,xmin,ctid,c0,c1,txid_current() from tmp_t0;
tableoid | cmax | xmax | cmin | xmin | ctid | c0 | c1 | txid_current
----------+------+------+------+------+-------+----+----+--------------
16393 | 0 | 0 | 0 | 1947 | (0,1) | 1 | 1 | 1951
16393 | 0 | 0 | 0 | 1949 | (0,2) | 2 | 2 | 1951
(2 rows)
postgres=# update tmp_t0 set c1='11' where c0='1';
UPDATE 1
postgres=# select tableoid,cmax,xmax,cmin,xmin,ctid,c0,c1,txid_current() from tmp_t0;
tableoid | cmax | xmax | cmin | xmin | ctid | c0 | c1 | txid_current
----------+------+------+------+------+-------+----+----+--------------
16393 | 0 | 0 | 0 | 1949 | (0,2) | 2 | 2 | 1951
16393 | 0 | 0 | 0 | 1951 | (0,3) | 1 | 11 | 1951
(2 rows)
可以看到,update之后 c0='1’的ctid由(0,1)变为(0,3),同时xmin也发生了相应的变化。
此时在 session 1 里不提交。
session 2 查看
postgres=# select tableoid,cmax,xmax,cmin,xmin,ctid,c0,c1,txid_current() from tmp_t0;
tableoid | cmax | xmax | cmin | xmin | ctid | c0 | c1 | txid_current
----------+------+------+------+------+-------+----+----+--------------
16393 | 0 | 1951 | 0 | 1947 | (0,1) | 1 | 1 | 1953
16393 | 0 | 0 | 0 | 1949 | (0,2) | 2 | 2 | 1953
(2 rows)
显示的c0='1’为update之前的值。postgresql 默认的事务隔离级别时 read committed,所以在session 2 是无法看到session 1已经更新,但是未提交的数据。
这个也侧面验证了 postgresql 的 mvcc 机制导致没有在行原地 update,而是变为相应的 deleted 标签和插入新行。
参考:
http://postgres.cn/docs/9.6/ddl-system-columns.html