PG中每個表都包含了一些系統字段,其中包括cmin和cmax。
cmin:插入該元組的指令在插入事務中的指令辨別(從0開始累加)
cmax:删除該元組的指令在插入事務中的指令辨別(從0開始累加)
可以在Select指令的輸出清單中顯式地指定系統字段。
點選(此處)折疊或打開
postgres=# create table tb1(c1 int);
CREATE TABLE
postgres=# begin;
BEGIN
postgres=# insert into tb1 values(1);
INSERT 0 1
postgres=# insert into tb1 values(2);
postgres=# select xmin,xmax,cmin,cmax,* from tb1;
xmin | xmax | cmin | cmax | c1
------+------+------+------+----
1055 | 0 | 0 | 0 | 1
1055 | 0 | 1 | 1 | 2
(2 行記錄)
postgres=# rollback;
ROLLBACK
cmin和cmax用于判斷同一個事務内的其他指令導緻的行版本變更是否可見。如果一個事務内的所有指令嚴格順序執行,那麼每個指令總能看到之前該事務内的所有變更,不需要使用指令辨別。然而一個事務記憶體在指令交替執行的情況,比如使用遊标進行查詢。Fetch遊标時看到的是聲明遊标時的資料快照而不是Fetch執行時,即聲明遊标後對資料的變更對該遊标不可見。
postgres=# insert into tb1(c1) values(1);
postgres=# declare tb1_v cursor for select xmin,xmax,cmin,cmax,* from tb1;
DECLARE CURSOR
postgres=# update tb1 set c1=2;
UPDATE 1
1060 | 0 | 1 | 1 | 2
(1 行記錄)
postgres=# fetch all from tb1_v;
1060 | 1060 | 0 | 0 | 1
行版本可見性的完整判斷邏輯課參考一下下面的代碼注釋
Source code comment in src/backend/utils/time/tqual.c:
* ((Xmin == my-transaction && inserted by the current transaction
* Cmin my-command && before this command, and
* (Xmax is null || the row has not been deleted, or
* (Xmax == my-transaction && it was deleted by the current transaction
* Cmax >= my-command))) but not before this command,
* || or
* (Xmin is committed && the row was inserted by a committed transaction, and
* (Xmax is null || the row has not been deleted, or
* (Xmax == my-transaction && the row is being deleted by this transaction
* Cmax >= my-command) || but it's not deleted "yet", or
* (Xmax != my-transaction && the row was deleted by another transaction
* Xmax is not committed)))) that has not been committed
出于減少系統字段大小的考慮,cmin和cmax在行版本的頭部使用同一個字段t_cid存儲,并和t_xvac交疊。是以通過系統字段看到的cmin和cmax的值總是相同的。
src/include/access/htup.h:
typedef struct HeapTupleFields
{
TransactionId t_xmin; /* inserting xact ID */
TransactionId t_xmax; /* deleting or locking xact ID */
union
{
CommandId t_cid; /* inserting or deleting command ID, or both */
TransactionId t_xvac; /* old-style VACUUM FULL xact ID */
} t_field3;
} HeapTupleFields;
當xmax為0,即行版本還沒有被删除時,t_cid代表插入指令的指令辨別。
當xmax不為0,且插入事務辨別xmin和删除事務辨別xmax不同時,t_cid代表删除指令的指令辨別。
當xmax不為0,且插入事務辨別xmin和删除事務辨別xmax相同時,t_cid代表組合指令辨別。在backend的私有空間存儲了組合指令辨別到實際的{cmin,cmax}組合的映射。
執行VACUUM FULL時不需要cmin和cmax,是以t_xvac可以和t_cid共用一個存儲空間。
我們注意到不是每次執行指令,都會導緻指令辨別增加。指令辨別的配置設定規則如下:
1)每個指令使用事務内全局的指令辨別計數器的目前值作為指令辨別。
2)事務開始時,指令辨別計數器被置為初值0
3)執行更新性的SQL(包括insert,update,delete,select ... for update等)時,在SQL執行後指令辨別計數器增1
4)當指令辨別計數器經過不斷累加又回到初值0時,報錯"cannot have more than 2^32-1 commands in a transaction"
由此可得出如下結論
1)非更新性的SQL和其後的第一個更新性SQL的指令辨別相同
2)同一個事務内的插入或删除行為對目前指令有效的條件是 cmin(或cmax) 指令辨別。
這就可以解釋[3.行版本可見性的判斷]中,指令辨別比較時,有時帶=,有時不帶。