PostgreSQL基础 - 系统列(隐含字段)

oid

一行的对象标识符(对象ID)。
该列只有在表使用WITH OIDS创建时或者default_with_oids配置变量被设置时才存在。
该列的类型为oid(与列名一致)。

PostgreSQL号称是对象关系数据库,
relation就是class,
tuple/row 就是 object (class instance)

pg_class表也是relation, 它的每一行都是object, 所以它的每一行都有oid, 由于这个表的特殊性, 这个表的每一行的oid又有一个名称叫tableoid

tableoid

包含这一行的表的OID。
该列是特别为从继承层次(见第 5.9 节)中选择的查询而准备,因为如果没有它将很难知道一行来自于哪个表。
tableoid可以与pg_class的oid列进行连接来获得表的名称。

xmin

The identity (transaction ID) of the inserting transaction for this row version.
(A row version is an individual state of a row; each update of a row creates a new row
version for the same logical row.)

插入该行版本的事务号(事务ID)。
(由于MVCC机制, PostgreSQL目前的物理存储里没有修改操作, 只有标记删除+插入, 同一个逻辑行可以有多个版本, 多个物理行, 当然只有最新一个版本是有效的逻辑行)
一个行版本是一个逻辑行的历史版本,对一个逻辑行的每一次更新都将创建一个新的行版本。

xmax

The identity (transaction ID) of the deleting transaction, or zero for an undeleted
row version. It is possible for this column to be nonzero in a visible row version. That
usually indicates that the deleting transaction hasn it committed yet, or that an attempted
deletion was rolled back.

删除事务的身份(事务ID),对于未删除的行版本为0。
对于一个可见的行版本,该列值也可能为非零。这通常表示删除事务还没有提交,或者一个删除尝试被回滚。
(因为修改其实是标记删除+插入, 所以修改也是一个"删除事务")

xmin记录的是当数据插入( Insert )时的事务ID,xmax记录的是当行上的数据有变动(delete or update )时的事务ID
xmax==0: 行没有被改过
xmax!=0: 行被改过或者行修改失败

cmin (command min, 指同一个事务里执行的第几条SQL命令, 第一条是0)

插入事务中的命令标识符(从0开始)。

cmax (command max)

删除事务中的命令标识符,或者为0。
据网友看源码后得出的结论: cmin和cmax是同一个字段!!!它们两个的值永远都是相同的。

ctid

行版本在其表中的物理位置。
注意尽管ctid可以被用来非常快速地定位行版本,但是一个行的ctid会在被更新或者被VACUUM FULL移动时改变。
因此,ctid不能作为一个长期行标识符。OID或者最好是一个用户定义的序列号才应该被用来标识逻辑行。

物理行ID和逻辑行ID

实验

查询语句参考:

select 
    txid_current(), 
    tableoid as table_oid, tableoid::regclass::text as table_name, 
    ctid as physical_id, logical_id, 
    xmin as create_xid,xmax as modified_xid,
    cmin as sql_number, cmax  as sql_number_alias, 
    notes 
from system_column_test ;

下面正式开始实验:


-- 打开终端1, 进入psql执行

create table system_column_test (logical_id int primary key, notes text);
insert into system_column_test select generate_series(1,5);
select txid_current(), ctid, logical_id, xmin,xmax,cmin, cmax, notes from system_column_test ;

begin;
insert into system_column_test values(6);
insert into system_column_test values(7);
insert into system_column_test values(8);
commit;
select txid_current(), ctid, logical_id, xmin,xmax,cmin, cmax, notes from system_column_test ;

begin;
insert into system_column_test values(9);
insert into system_column_test values(10);
commit;
select txid_current(), ctid, logical_id, xmin,xmax,cmin, cmax, notes from system_column_test ;

-- 可以看到, xmax 值都是为0,xmin则有3个(1~5相同, 6~8相同, 9~10相同)
-- 那什么情况下 xmax 值不为0呢,有几种情况,接下来看

-- 1. deleting事务未提交
-- 1.1 开启终端2, 进入psql执行
begin;
select txid_current();
delete from system_column_test where logical_id=3;
-- 1.2 切换到终端1
select xmin,xmax,ctid,* from system_column_test;
-- 可以看到logical_id=3的记录的 xmax 变为 非0了。
-- 1.3 切换到终端2
commit;
-- 1.4 切换到终端1
select xmin,xmax,ctid,* from system_column_test;
-- logical_id=3的记录已经被删除

-- 2. delete 事务 rollback
begin;
select txid_current();  
delete from system_column_test where logical_id=4;
rollback;
select xmin,xmax,ctid,* from system_column_test;
-- 可以看到logical_id=4的记录的 xmax 变为 非0了。
-- 注意: ctid没有发生变化

-- 3. updating 事务 rollback
begin;
select txid_current();
update system_column_test set  notes = 'updated' where logical_id=5;
rollback;
select xmin,xmax,ctid,* from system_column_test;
-- 可以看到logical_id=5的记录的 xmax 变为 非0了。
-- 同时, ctid同样没有发生变化
-- 用pageinspect插件查看物理页看看页面情况。
select * from heap_page_items(get_raw_page('system_column_test',0)) order by lp_off desc;
-- 用pageinspect看到的ctid却是变化了的...

-- 4. update 事务 commit
-- 注意: 这个不是xmax 值不为0的情况, 但是有参考意义, 所以一同列出来
begin;
select txid_current();
update system_column_test set  notes = 'updated' where logical_id=2;
commit;
select xmin,xmax,ctid,* from system_column_test;
-- 可以看到logical_id=2的记录的 xmax 还是 0。
-- 不过, ctid发生变化
-- 用pageinspect插件查看物理页看看页面情况。
select * from heap_page_items(get_raw_page('system_column_test',0)) order by lp_off desc;
-- 用pageinspect看到总共有12行, 10个有效行, 2个dead行

操作过程中的输出就不贴了, 只贴出最终结果:


postgres=# select xmin,xmax,ctid,* from system_column_test;
 xmin | xmax |  ctid  | logical_id |  notes
------+------+--------+------------+---------
  898 |    0 | (0,1)  |          1 |
  898 |  905 | (0,4)  |          4 |
  898 |  906 | (0,5)  |          5 |
  900 |    0 | (0,6)  |          6 |
  900 |    0 | (0,7)  |          7 |
  900 |    0 | (0,8)  |          8 |
  902 |    0 | (0,9)  |          9 |
  902 |    0 | (0,10) |         10 |
  907 |    0 | (0,12) |          2 | updated
(9 rows)

postgres=# select lp,lp_off,t_xmin, t_xmax, t_field3 as cmin_cmax, t_ctid, t_infomask, t_infomask2,t_data from heap_page_items(get_raw_page('system_column_test',0)) order by lp_off desc;
 lp | lp_off | t_xmin | t_xmax | cmin_cmax | t_ctid | t_infomask | t_infomask2 |           t_data
----+--------+--------+--------+-----------+--------+------------+-------------+----------------------------
  1 |   8160 |    898 |      0 |         0 | (0,1)  |       2305 |           2 | \x01000000
  2 |   8128 |    898 |    907 |         0 | (0,12) |       1281 |       16386 | \x02000000
  3 |   8096 |    898 |    904 |         0 | (0,3)  |       1281 |        8194 | \x03000000
  4 |   8064 |    898 |    905 |         0 | (0,4)  |       2305 |        8194 | \x04000000
  5 |   8032 |    898 |    906 |         0 | (0,11) |       2305 |       16386 | \x05000000
  6 |   8000 |    900 |      0 |         0 | (0,6)  |       2305 |           2 | \x06000000
  7 |   7968 |    900 |      0 |         1 | (0,7)  |       2305 |           2 | \x07000000
  8 |   7936 |    900 |      0 |         2 | (0,8)  |       2305 |           2 | \x08000000
  9 |   7904 |    902 |      0 |         0 | (0,9)  |       2305 |           2 | \x09000000
 10 |   7872 |    902 |      0 |         1 | (0,10) |       2305 |           2 | \x0a000000
 11 |   7832 |    906 |      0 |         0 | (0,11) |      10754 |       32770 | \x050000001175706461746564
 12 |   7792 |    907 |      0 |         0 | (0,12) |      10498 |       32770 | \x020000001175706461746564
(12 rows)

参考文档:

  1. 官方文档-系统列
  2. postgresql 的系统列 oid、tableoid、xmin、cmin、xmax、cmax
  3. 表级隐含字段: Xmin 和 Xmax
  4. postgresql里cmin与cmax有何不同.md
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容