天天看點

[2021-06-17]PostgreSQL事物送出日志資訊clog解析

       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]$