postgresql隐藏字段 ctid xmin xmax cmin cmax oid
postgresql隐藏字段
在 PostgreSQL 中,当我们创建一个数据表时,数据库会隐式增加几个字段。这些字段由数据库系自动维护,用户一般不会感知
tableoid
ctid
xmin
xmax
cmin
cmax
oid
tableoid
包含这一行的表的OID。该列是特别为从继承层次(见第 5.10 节)中选择的查询而准备,因为如果没有它将很难知道一行来自于哪个表。tableoid可以与pg_class的oid列进行连接来获得表的名称。
xmin
插入该行版本的事务身份(事务ID)。一个行版本是一个行的一个特别版本,对一个逻辑行的每一次更新都将创建一个新的行版本。
cmin
插入事务中的命令标识符(从0开始)。
xmax
删除事务的身份(事务ID),对于未删除的行版本为0。对于一个可见的行版本,该列值也可能为非零。这通常表示删除事务还没有提交,或者一个删除尝试被回滚。
cmax
删除事务中的命令标识符,或者为0。
ctid
行版本在其表中的物理位置。注意尽管ctid可以被用来非常快速地定位行版本,但是一个行的ctid会在被更新或者被VACUUM FULL移动时改变。因此,ctid不能作为一个长期行标识符。 应使用主键来标识逻辑行。
select attname, attnum, atttypid::regtype from pg_attribute where attrelid = 'test_abc'::regclass; //::双冒号表示类型转换,即将atttypid字段转换为regtype类型数据 regclass表示OID的别名 // pg_attribute该系统表存储所有表(包括系统表,如pg_class)的字段信息。数据库中的每个表的每个字段在pg_attribute表中都有一行记录。 attname 字段名 atttypid 字段的数据类型。attnum 字段的编号,普通字段是从1开始计数的。系统字段,如oid,是任意的负数。
select t.tableoid, t.id,t.name, c.relname from test_abc t join pg_class c on (c.oid = t.tableoid);
ctid表示数据行的在它所处的表内的物理位置。ctid字段的类型是是tid。尽管tid可以非常快的定位数据行。但是每次vacuum full之后。数据行在块内的物理位置会移动。即ctid会发生变化。所以ctid不能长期作为行标识符,ctid由两个数字组成,第一个数字表示物理块号,第二个数字表示在物理块中的行数
xmin 代表了该行版本(row version )的插入事务 ID(XID)。行版本是数据行的具体状态,每次更新操作都会为相同的逻辑行创建一个新的行版本
mydb=# create table test_abc(id int,name varchar(20));
CREATE TABLE
mydb=# \d+ test_abc;
Table "public.test_abc"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+-----------------------+-----------+----------+---------+----------+--------------+-------------
id | integer | | | | plain | |
name | character varying(20) | | | | extended | |
mydb=# insert into test_abc values(1,'aaa'),(2,'bbb'),(3,'ccc');
INSERT 0 3
mydb=# select * from test_abc;l
id | name
----+------
1 | aaa
2 | bbb
3 | ccc
mydb=# select id,name,ctid,xmin,xmax,cmax,cmin from test_abc;
id | name | ctid | xmin | xmax | cmax | cmin
----+------+-------+------+------+------+------
1 | aaa | (0,1) | 641 | 0 | 0 | 0
2 | bbb | (0,2) | 641 | 0 | 0 | 0
3 | ccc | (0,3) | 641 | 0 | 0 | 0
xmin 字段可以用于查看数据行的插入时间:
select id,name,to_char(pg_xact_commit_timestamp(xmin) ,'YYYY-MM-DD HH24:MI:SS') AS insert_time from test_abc;
记录transaction的提交时间。
注意:只有当"track_commit_timestamp"参数是on时,函数pg_xact_commit_timestamp(xid)、pg_last_committed_xact()才能获得已提交事务的时间值。该参数需要重启
xmax 字段代表了删除改行的事务 ID,对于未删除的行版本显示为 0。非零的 xmax 通常意味着删除事务还没有提交,或者删除操作被回滚。
查看当前事务ID
select txid_current();
txid_current
--------------
645
(1 row)
mydb=# select id,name,ctid,xmin,xmax,cmax,cmin from test_abc;
id | name | ctid | xmin | xmax | cmax | cmin
----+------+-------+------+------+------+------
1 | aaa | (0,1) | 641 | 0 | 0 | 0
2 | bbb | (0,2) | 641 | 0 | 0 | 0
3 | ccc | (0,3) | 641 | 0 | 0 | 0
4 | ddd | (0,4) | 642 | 0 | 0 | 0
mydb=# update test_abc set name='ddd' where id=1;
UPDATE 1
mydb=# select * from test_abc;
id | name
----+------
2 | bbb
3 | ccc
4 | ddd
1 | ddd
(4 rows)
mydb=# select id,name,ctid,xmin,xmax,cmax,cmin from test_abc;
id | name | ctid | xmin | xmax | cmax | cmin
----+------+-------+------+------+------+------
2 | bbb | (0,2) | 641 | 0 | 0 | 0
3 | ccc | (0,3) | 641 | 0 | 0 | 0
4 | ddd | (0,4) | 642 | 0 | 0 | 0
1 | ddd | (0,5) | 645 | 0 | 0 | 0
ctid以改变成(0,5)
从当前事务中已经看不到aaa那条记录,但是1,ddd这条被修改后的xmin为645为当前的事务ID,
从事务2的窗口查看
mydb=# begin;
BEGIN
mydb=# select txid_current();
txid_current
--------------
646
(1 row)
mydb=# select id,name,ctid,xmin,xmax,cmax,cmin
id | name | ctid | xmin | xmax | cmax | cmin
----+------+-------+------+------+------+------
1 | aaa | (0,1) | 641 | 645 | 0 | 0
2 | bbb | (0,2) | 641 | 0 | 0 | 0
3 | ccc | (0,3) | 641 | 0 | 0 | 0
4 | ddd | (0,4) | 642 | 0 | 0 | 0
aaa的记录xmin为641是该条记录插入式的事务id,xmax为该条记录删除的事务ID,即事务1的事务ID,也就是说update在pg中处理为删除老的记录,在插入一条新的记录,删除老的记录时,老的记录的xmax被改成删除操作所在的事务ID
在同一事务中,如果不通数据记录的xmax>xmin,则说明xmax的数据在xmin数据之后
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~