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)
参考文档:
- 官方文档-系统列
- postgresql 的系统列 oid、tableoid、xmin、cmin、xmax、cmax
- 表级隐含字段: Xmin 和 Xmax
- postgresql里cmin与cmax有何不同.md