PG事物送出之後,會把事務的狀态資訊寫入到clog(pg_xact)檔案中,當第一次通路事物涉及到的行時,會讀取clog記錄的事物狀态資訊來判斷事物是否送出或者abort,并且更新t_infomask的狀态,這樣下次再通路該行資料時通過判斷t_infomask就知道不再需要通路clog了。具體t_infomask的具體值和意義,可以參考https://blog.csdn.net/m15217321304/article/details/110870837
--//源碼位置 src/backend/access/transam/clog.c
--//xid事物的狀态資訊可以在頭檔案檢視
--//頭檔案位置src/include/access/clog.h
/*
* Possible transaction statuses --- note that all-zeroes is the initial
* state.
*
* A "subcommitted" transaction is a committed subtransaction whose parent
* hasn't committed or aborted yet.
*/
typedef int XidStatus;
#define TRANSACTION_STATUS_IN_PROGRESS 0x00
#define TRANSACTION_STATUS_COMMITTED 0x01
#define TRANSACTION_STATUS_ABORTED 0x02
#define TRANSACTION_STATUS_SUB_COMMITTED 0x03
--//源碼位置 src/backend/access/transam/clog.c
/*
* Defines for CLOG page sizes. A page is the same BLCKSZ as is used
* everywhere else in Postgres.
*
* Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
* CLOG page numbering also wraps around at 0xFFFFFFFF/CLOG_XACTS_PER_PAGE,
* and CLOG segment numbering at
* 0xFFFFFFFF/CLOG_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT. We need take no
* explicit notice of that fact in this module, except when comparing segment
* and page numbers in TruncateCLOG (see CLOGPagePrecedes).
*/
/* We need two bits per xact, so four xacts fit in a byte */
#define CLOG_BITS_PER_XACT 2
#define CLOG_XACTS_PER_BYTE 4
#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
#define CLOG_XACT_BITMASK ((1 << CLOG_BITS_PER_XACT) - 1)
##CLOG_BITS_PER_XACT 代表一個事務2個位元組 , 一個事務需要兩個位元組表示,是以一個1位元組(8個bit位)可以存放8/2=4個事物
##CLOG_XACTS_PER_BYTE 代表一個位元組可以存放4個事物
##CLOG_XACTS_PER_PAGE 代表一個page可以存放多少事物 ,比如一個塊8K, 一個位元組可以存放4個事物, 是以一個page=8192*4
##CLOG_XACT_BITMASK 代表CLOG_BITS_PER_XACT左移一位,CLOG_BITS_PER_XACT原值為2,二進制為0010,左移一位0100,然後0100轉換10進制為3,3 - 1 =2,轉換2進制為0011
#define SLRU_PAGES_PER_SEGMENT 32
代入公式中:
N = 0xFFFFFFFF/CLOG_XACTS_PER_PAGE/SLRU_PAGES_PER_SEGMENT
= 0xFFFFFFFF/(8192*4)/32
= 4096
--//每個segment有32個Pages(SLRU_PAGES_PER_SEGMENT = 32),則每個segment file大小為8K*32=256K.
給定一個事務号,如何擷取該事務對應的狀态?
PG首先通過該事務号獲得該事務狀态存儲在clog中哪個page,然後再根據下面的公式,計算存儲事物狀态的偏移量
#define TransactionIdToPage(xid) ((xid) / (TransactionId) CLOG_XACTS_PER_PAGE)
#define TransactionIdToPgIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_PAGE)
#define TransactionIdToByte(xid) (TransactionIdToPgIndex(xid) / CLOG_XACTS_PER_BYTE)
#define TransactionIdToBIndex(xid) ((xid) % (TransactionId) CLOG_XACTS_PER_BYTE)
如給定事務号100,根據上述公式可得到:
Page = 100 / (8192*4) = 0 —> 第0号page
PageIndex = 100 % (8192*4) = 100 —> Page内偏移
ByteInPage = 100 / 4 = 25 —> 該Page内的第25個Byte
ByteIndex = 100 % 4 = 0 —> 該位元組中的首2bits
--//上面提到一個segment大小為8K*32=256K
--//我這裡就遇到這麼一個問題,我的xid事物号很大,如果直接套用上面的公式,那麼在xact檔案中找不到對應的檔案
下面通過實際案例驗證
--//session 1
postgres=# select txid_current();
txid_current
--------------
1048700
(1 row)
postgres=#
--//先不要送出
--//按照公式
--//1048700/(8192*4)
postgres=# select 1048700/(8192*4);
?column?
----------
32
(1 row)
postgres=#
--//但是xact檔案裡面沒有超過32k大小的檔案
[[email protected] pg_xact]$ ls -ltr
total 16
-rw------- 1 postgres postgres 8192 Jan 19 07:47 0000
-rw------- 1 postgres postgres 8192 Jun 9 00:07 0001
[[email protected] pg_xact]$
--//一個段 32個page ,一個page可以存放page*4個事物, 是以一個段最多可以存放 32*8192*4= 1048576
--//是以,如果一個xid大于了1048700,那麼需要使用xid%(8192*4*32)(無論xid大小是多少,使用這個公式都沒有問題)
postgres=# select 1048700/(8192*4*32);
?column?
----------
1
(1 row)
postgres=#
—-//是以,xid事物落到了0001檔案
--//偏移量為1048694%(8192*4*32)
postgres=# select 1048700%(8192*4*32);
?column?
----------
124
(1 row)
--//換算偏移量對應的位置
postgres=# select 124/4;
?column?
----------
31
(1 row)
postgres=#
--//換算在31位元組處的具體位置(1個位元組8個bit,2個bit代表一個事務)
postgres=# select 124%4;
?column?
----------
0
(1 row)
postgres=#
--//說明在31位元組的前2bit位
--//使用hexdump檢視内容,目前還沒有送出
[[email protected] pg_xact]$ hexdump -C 0001 -s 31 -n 1
0000001f 00 |.|
00000020
[[email protected] pg_xact]$
--//對應的進制碼為 00 00 00 00
--//session 1送出會話并checkpoint
postgres=# commit;
COMMIT
postgres=# checkpoint;
CHECKPOINT
postgres=# \q
[[email protected] pg_xact]$
--//再次hexdump檢視
[[email protected] pg_xact]$ hexdump -C 0001 -s 31 -n 1
0000001f 01 |.|
00000020
[[email protected] pg_xact]$
--//對應二進制碼為 00 00 00 01
--//session 1 再次開啟一個新事務,最後復原
postgres=# select txid_current();
txid_current
--------------
1048701
(1 row)
postgres=# abort;
ROLLBACK
postgres=# checkpoint;
CHECKPOINT
postgres=#
--//查詢clog的資料
--//#define TRANSACTION_STATUS_ABORTED 0x02
--//理論上0x2轉換2進制為 10,那麼31位元組的bit 碼應如下:
--//00 00 10 01 轉換16進制為9
--//使用hexdump檢視
[[email protected] pg_xact]$ hexdump -C 0001 -s 31 -n 1
0000001f 09 |.|
00000020
[[email protected] pg_xact]$
--//session 1 再次開啟一個新事務,然後復原
postgres=# begin;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1048702
(1 row)
postgres=# abort;
ROLLBACK
postgres=# checkpoint;
CHECKPOINT
postgres=#
--//理論上2進制碼為 00 10 10 01 ,轉換16進制 29
--//使用hexdump驗證
[[email protected] pg_xact]$ hexdump -C 0001 -s 31 -n 1
0000001f 29 |)|
00000020
[[email protected] pg_xact]$
--//session 1 再次開啟一個新事務,最後送出
postgres=# begin;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1048703
(1 row)
postgres=# commit;
COMMIT
postgres=# checkpoint;
CHECKPOINT
postgres=#
--//這個時候2進制碼應該為 01 10 10 01,轉換16進制為 69
--//使用hexdump驗證
[[email protected] pg_xact]$ hexdump -C 0001 -s 31 -n 1
0000001f 69 |i|
00000020
[[email protected] pg_xact]$
--//如果session 1再開啟一個會話,無論事務送出還是復原,都不會再影響31位元組處的資料
--//session 1開啟一個會話,送出
postgres=# begin;
BEGIN
postgres=# select txid_current();
txid_current
--------------
1048704
(1 row)
postgres=# commit;
COMMIT
postgres=# checkpoint;
CHECKPOINT
postgres=#
--//使用hexdump驗證
[[email protected] pg_xact]$ hexdump -C 0001 -s 31 -n 1
0000001f 69 |i|
00000020
[[email protected] pg_xact]$
--//如果驗證32位元組處的資料,2進制碼應該是 00 00 00 01,轉換16進制為01
[[email protected] pg_xact]$ hexdump -C 0001 -s 32 -n 1
00000020 01 |.|
00000021
[[email protected] pg_xact]$