PostgreSQL 中的系统字段:tableoid、xmin、xmax、cmin、cmax、ctid
文章目錄
- tableoid
- ctid
- xmin
- xmax
- cmin
- cmax
- oid
- 總結
大家好!我是只談技術不剪發的 Tony 老師。今天我們來談談 PostgreSQL 數據表中幾個隱藏的系統字段和它們的作用。
在 PostgreSQL 中,當我們創建一個數據表時,數據庫會隱式增加幾個系統字段。這些字段由系統進行維護,用戶一般不會感知它們的存在。例如,以下語句創建了一個簡單的表:
create table test(col integer);insert into test(col) values (1),(2),(3);從定義上來看,表 test 中只有一個字段;但是當我們查詢數據字典表 pg_attribute 時,結果卻不是如此:
hrdb=> select version();version ---------------------------------------------------------------------------------------------------------PostgreSQL 12.3 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39), 64-bit (1 row)hrdb=> select attname, attnum, atttypid::regtype hrdb-> from pg_attribute hrdb-> where attrelid = 'test'::regclass;attname | attnum | atttypid ----------+--------+----------tableoid | -6 | oidcmax | -5 | cidxmax | -4 | xidcmin | -3 | cidxmin | -2 | xidctid | -1 | tidcol | 1 | integer (7 rows)查詢結果顯示,表 test 中一共包含 7 個字段。PostgreSQL 為我們增加了 6 個額外的系統字段,它們的 attnum 屬性都是負數。
下面讓我們分別看看這些系統字段的作用。
tableoid
tableoid 字段代表了數據所在表的對象 id(OID),也就是數據字典表 pg_class 中與該表信息相關的數據行。
hrdb=> select oid, relname from pg_class where relname = 'test';oid | relname -------+---------90277 | test (1 row)hrdb=> select t.tableoid, t.col, c.relname hrdb-> from test t hrdb-> join pg_class c on (c.oid = t.tableoid);tableoid | col | relname ----------+-----+---------90277 | 1 | test90277 | 2 | test90277 | 3 | test (3 rows)tableoid 的另一個用途就是在涉及分區表查詢或者 UNION 操作時標識數據行所在的具體表。例如存在以下分區表:
create table part_t (id integer) partition by hash (id); create table part_t_p1 partition of part_t for values with (modulus 4, remainder 0); create table part_t_p2 partition of part_t for values with (modulus 4, remainder 1); create table part_t_p3 partition of part_t for values with (modulus 4, remainder 2); create table part_t_p4 partition of part_t for values with (modulus 4, remainder 3);insert into part_t select generate_series(1,100);我們可以通過以下查詢返回每行數據所在的分區:
hrdb=> select tableoid::regclass, id hrdb-> from part_t hrdb-> order by id hrdb-> limit 10;tableoid | id -----------+----part_t_p1 | 1part_t_p3 | 2part_t_p2 | 3part_t_p4 | 4part_t_p2 | 5part_t_p4 | 6part_t_p4 | 7part_t_p2 | 8part_t_p2 | 9part_t_p4 | 10 (10 rows)對于集合操作 UNION、INTERSECT、EXCEPT 也是如此:
hrdb=> select tableoid::regclass, col from test hrdb-> union all hrdb-> select tableoid::regclass, id from part_t where id < 4 hrdb-> order by 2;tableoid | col -----------+-----test | 1part_t_p1 | 1test | 2part_t_p3 | 2test | 3part_t_p2 | 3 (6 rows)ctid
ctid 字段代表了數據行在表中的物理位置,也就是行標識(tuple identifier),由一對數值組成(塊編號和行索引)。ctid 類似于 Oracle 中的偽列 ROWID。
ctid 可以用于快速查找表中的數據行,也可以用于修復數據損壞。另外,它也可以用于查找并刪除表中的重復數據。例如:
insert into test(col) values (1),(2),(3);hrdb=> select ctid, * from test;ctid | col -------+-----(0,1) | 1(0,2) | 2(0,3) | 3(0,4) | 1(0,5) | 2(0,6) | 3 (6 rows)我們為 test 表插入了 3 條重復的數據。接下來利用 ctid 刪除重復的數據:
hrdb=> delete from test hrdb-> where ctid not in hrdb-> ( hrdb(> select max(ctid) hrdb(> from test hrdb(> group by col hrdb(> ); DELETE 3需要注意的是,ctid 的值有可能會改變(例如 VACUUM FULL);因此,ctid 不適合作為一個長期的行標識,應該使用主鍵作為行的邏輯標識。
xmin
xmin 代表了該行版本(row version )的插入事務 ID(XID)。行版本是數據行的具體狀態,每次更新操作都會為相同的邏輯行創建一個新的行版本(多版本并發控制,MVCC)。事務 ID 是一個 32 bit 數字。
我們繼續為 test 表插入幾條數據,并查看它們的 xmin:
hrdb=> insert into test(col) values(4); INSERT 0 1 hrdb=> insert into test(col) values(5); INSERT 0 1hrdb=> select xmin,col from test;xmin | col ------+-----2852 | 12852 | 22852 | 32854 | 42855 | 5 (5 rows)xmin 字段可以用于查看數據行的插入時間:
hrdb=> select col, hrdb-> to_char(pg_xact_commit_timestamp(xmin) ,'YYYY-MM-DD HH24:MI:SS') AS insert_time hrdb-> from test;col | insert_time -----+---------------------1 | 2020-05-28 16:52:082 | 2020-05-28 16:52:083 | 2020-05-28 16:52:084 | 2020-05-28 17:03:335 | 2020-05-28 17:03:35 (5 rows)注意,系統函數 pg_xact_commit_timestamp 需要將配置參數 track_commit_timestamp 設置為 on 才能使用。
xmax
xmax 字段代表了刪除改行的事務 ID,對于未刪除的行版本顯示為 0。非零的 xmax 通常意味著刪除事務還沒有提交,或者刪除操作被回滾。
我們查看一下 test 表中的 xmax:
hrdb=> select txid_current();txid_current --------------2858 (1 row)hrdb=> select xmax, col from test;xmax | col ------+-----0 | 10 | 20 | 30 | 40 | 5 (5 rows)然后打開另一個會話,在事務中修改 test 表中的數據:
-- 會話 2 hrdb=> update test hrdb-> set col= col*2; UPDATE 5回到第一個會話,再次查看 xmax:
hrdb=> select xmax, col from test;xmax | col ------+-----2858 | 12858 | 22858 | 32858 | 42858 | 5 (5 rows)2858 是第二個會話的事務 ID,它是刪除這些行版本的事務。PostgreSQL 中的 UPDATE 相當于 DELETE 加 INSERT。
將第二個事務回滾:
-- 會話 2 hrdb=> rollback; ROLLBACK如果再次查詢 test 表中的 xmax,仍然返回 2858。
xmax 還有可能表示當前正在占用行鎖的事務 ID,利用 PostgreSQL 擴展插件 pageinspect 可以獲取詳細信息:
create extension pageinspect;select t.col,t.xmaxcasewhen (t_infomask & 128)::boolean then 'LOCK'when (t_infomask & 1024)::boolean then 'COMMITTED'when (t_infomask & 2048)::boolean then 'ROLLBACKED'when (t_infomask & 4096)::boolean then 'MULTI XACT'end as xmax_info from test t left outer join heap_page_items(get_raw_page('test', 0)) hp on (t.ctid = hp.t_ctid) where hp.t_xmax = t.xmax;cmin
cmin 代表了插入事務中的命令標識符(從 0 開始)。命令標識符是一個 32 bit 數字。
cmax
cmax 代表了刪除事務中的命令標識符,或者 0。
我們先查看一下 test 表中的
hrdb=> select cmin, cmax, col from test;cmin | cmax | col ------+------+-----0 | 0 | 10 | 0 | 20 | 0 | 30 | 0 | 40 | 0 | 5 (5 rows)然后在事務中修改數據:
hrdb=> begin; BEGIN hrdb=> select txid_current();txid_current --------------2859 (1 row)hrdb=> insert into test(col) values(6); INSERT 0 1 hrdb=> insert into test(col) values(7); INSERT 0 1 hrdb=> insert into test(col) values(8); INSERT 0 1hrdb=> select cmin, cmax, col from test;cmin | cmax | col ------+------+-----0 | 0 | 10 | 0 | 20 | 0 | 30 | 0 | 40 | 0 | 50 | 0 | 61 | 1 | 72 | 2 | 8 (8 rows)然后刪除一條記錄:
hrdb=> delete from test where col=1; DELETE 1此時,從另一個會話中查看:
-- 會話 2 hrdb=> select cmin, cmax, col from test;cmin | cmax | col ------+------+-----3 | 3 | 10 | 0 | 20 | 0 | 30 | 0 | 40 | 0 | 5 (5 rows)oid
如果使用 PostgreSQL 11 或者更早版本,還有一個隱藏的系統字段:oid。它代表了數據行的對象 ID,只有當創建表時使用了 WITH OIDS 選項或者配置參數 default_with_oids 設置為 true 時才會創建這個字段。
從 PostgreSQL 12 開始,不再支持 WITH OIDS 選項,oid 只用于系統內部。
總結
PostgreSQL 中的每個表都包含了 6 個隱藏的系統字段,可以用于獲取關于數據行的一些內部信息。這些字段名稱不能用于創建普通的字段,即使使用雙引號包含也不可以。
如果你點擊了收藏?,請不要忘了關注??、評論📝、點贊👍!
總結
以上是生活随笔為你收集整理的PostgreSQL 中的系统字段:tableoid、xmin、xmax、cmin、cmax、ctid的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: postgresql源码学习(十三)——
- 下一篇: 对接天猫精灵X1 (https 的申请)