溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL 源碼解讀(111)- WAL#7(Insert&WAL - XLogRe...

發(fā)布時(shí)間:2020-08-04 14:08:57 來源:ITPUB博客 閱讀:339 作者:husthxd 欄目:關(guān)系型數(shù)據(jù)庫

本節(jié)重點(diǎn)跟蹤分析了XLogRecordAssemble函數(shù)中對(duì)FPW(full-page-write)的處理過程。

一、數(shù)據(jù)結(jié)構(gòu)

全局靜態(tài)變量
XLogRecordAssemble使用的全局變量包括hdr_rdt/hdr_scratch/rdatas等.


/* flags for the in-progress insertion */
//用于插入過程中的標(biāo)記信息
static uint8 curinsert_flags = 0;

/*
 * These are used to hold the record header while constructing a record.
 * 'hdr_scratch' is not a plain variable, but is palloc'd at initialization,
 * because we want it to be MAXALIGNed and padding bytes zeroed.
 * 在構(gòu)建XLOG Record時(shí)通常會(huì)存儲(chǔ)記錄的頭部信息.
 * 'hdr_scratch'并不是一個(gè)普通(plain)變量,而是在初始化時(shí)通過palloc初始化,
 *   因?yàn)槲覀兿M撟兞恳呀?jīng)是MAXALIGNed并且已被0x00填充.
 *
 * For simplicity, it's allocated large enough to hold the headers for any
 * WAL record.
 * 簡(jiǎn)單起見,該變量預(yù)先會(huì)分配足夠大的空間用于存儲(chǔ)所有WAL Record的頭部信息.
 */
static XLogRecData hdr_rdt;
static char *hdr_scratch = NULL;

#define SizeOfXlogOrigin    (sizeof(RepOriginId) + sizeof(char))

#define HEADER_SCRATCH_SIZE \
    (SizeOfXLogRecord + \
     MaxSizeOfXLogRecordBlockHeader * (XLR_MAX_BLOCK_ID + 1) + \
     SizeOfXLogRecordDataHeaderLong + SizeOfXlogOrigin)
/*
 * An array of XLogRecData structs, to hold registered data.
 * XLogRecData結(jié)構(gòu)體數(shù)組,存儲(chǔ)已注冊(cè)的數(shù)據(jù).
 */
static XLogRecData *rdatas;
static int  num_rdatas;         /* entries currently used */
//已分配的空間大小
static int  max_rdatas;         /* allocated size */
//是否調(diào)用XLogBeginInsert函數(shù)
static bool begininsert_called = false;

static XLogCtlData *XLogCtl = NULL;

/* flags for the in-progress insertion */
static uint8 curinsert_flags = 0;

/*
 * A chain of XLogRecDatas to hold the "main data" of a WAL record, registered
 * with XLogRegisterData(...).
 * 存儲(chǔ)WAL Record "main data"的XLogRecDatas數(shù)據(jù)鏈
 */
static XLogRecData *mainrdata_head;
static XLogRecData *mainrdata_last = (XLogRecData *) &mainrdata_head;
//鏈中某個(gè)位置的mainrdata大小
static uint32 mainrdata_len; /* total # of bytes in chain */

/*
 * ProcLastRecPtr points to the start of the last XLOG record inserted by the
 * current backend.  It is updated for all inserts.  XactLastRecEnd points to
 * end+1 of the last record, and is reset when we end a top-level transaction,
 * or start a new one; so it can be used to tell if the current transaction has
 * created any XLOG records.
 * ProcLastRecPtr指向當(dāng)前后端插入的最后一條XLOG記錄的開頭。
 * 它針對(duì)所有插入進(jìn)行更新。
 * XactLastRecEnd指向最后一條記錄的末尾位置 + 1,
 *   并在結(jié)束頂級(jí)事務(wù)或啟動(dòng)新事務(wù)時(shí)重置;
 *   因此,它可以用來判斷當(dāng)前事務(wù)是否創(chuàng)建了任何XLOG記錄。
 *
 * While in parallel mode, this may not be fully up to date.  When committing,
 * a transaction can assume this covers all xlog records written either by the
 * user backend or by any parallel worker which was present at any point during
 * the transaction.  But when aborting, or when still in parallel mode, other
 * parallel backends may have written WAL records at later LSNs than the value
 * stored here.  The parallel leader advances its own copy, when necessary,
 * in WaitForParallelWorkersToFinish.
 * 在并行模式下,這可能不是完全是最新的。
 * 在提交時(shí),事務(wù)可以假定覆蓋了用戶后臺(tái)進(jìn)程或在事務(wù)期間出現(xiàn)的并行worker進(jìn)程的所有xlog記錄。
 * 但是,當(dāng)中止時(shí),或者仍然處于并行模式時(shí),其他并行后臺(tái)進(jìn)程可能在較晚的LSNs中寫入了WAL記錄,
 *   而不是存儲(chǔ)在這里的值。
 * 當(dāng)需要時(shí),并行處理進(jìn)程的leader在WaitForParallelWorkersToFinish中會(huì)推進(jìn)自己的副本。
 */
XLogRecPtr  ProcLastRecPtr = InvalidXLogRecPtr;
XLogRecPtr  XactLastRecEnd = InvalidXLogRecPtr;
XLogRecPtr XactLastCommitEnd = InvalidXLogRecPtr;

/* For WALInsertLockAcquire/Release functions */
//用于WALInsertLockAcquire/Release函數(shù)
static int  MyLockNo = 0;
static bool holdingAllLocks = false;

宏定義
XLogRegisterBuffer函數(shù)使用的flags

/* flags for XLogRegisterBuffer */
//XLogRegisterBuffer函數(shù)使用的flags
#define REGBUF_FORCE_IMAGE  0x01    /* 強(qiáng)制執(zhí)行full-page-write;force a full-page image */
#define REGBUF_NO_IMAGE     0x02    /* 不需要FPI;don't take a full-page image */
#define REGBUF_WILL_INIT    (0x04 | 0x02)   /* 在回放時(shí)重新初始化page(表示NO_IMAGE);
                                             * page will be re-initialized at
                                             * replay (implies NO_IMAGE) */
#define REGBUF_STANDARD     0x08    /* 標(biāo)準(zhǔn)的page layout(數(shù)據(jù)在pd_lower和pd_upper之間的數(shù)據(jù)會(huì)被跳過)
                                     * page follows "standard" page layout,
                                     * (data between pd_lower and pd_upper
                                     * will be skipped) */
#define REGBUF_KEEP_DATA    0x10    /* include data even if a full-page image
                                      * is taken */
/*
 * Flag bits for the record being inserted, set using XLogSetRecordFlags().
 */
#define XLOG_INCLUDE_ORIGIN     0x01    /* include the replication origin */
#define XLOG_MARK_UNIMPORTANT   0x02    /* record not important for durability */    

XLogRecData
xloginsert.c中的函數(shù)構(gòu)造一個(gè)XLogRecData結(jié)構(gòu)體鏈用于標(biāo)識(shí)最后的WAL記錄

/*
 * The functions in xloginsert.c construct a chain of XLogRecData structs
 * to represent the final WAL record.
 * xloginsert.c中的函數(shù)構(gòu)造一個(gè)XLogRecData結(jié)構(gòu)體鏈用于標(biāo)識(shí)最后的WAL記錄
 */
typedef struct XLogRecData
{
    //鏈中的下一個(gè)結(jié)構(gòu)體,如無則為NULL
    struct XLogRecData *next;   /* next struct in chain, or NULL */
    //rmgr數(shù)據(jù)的起始地址
    char       *data;           /* start of rmgr data to include */
    //rmgr數(shù)據(jù)大小
    uint32      len;            /* length of rmgr data to include */
} XLogRecData;

registered_buffer
對(duì)于每一個(gè)使用XLogRegisterBuffer注冊(cè)的每個(gè)數(shù)據(jù)塊,填充到registered_buffer結(jié)構(gòu)體中


/*
 * For each block reference registered with XLogRegisterBuffer, we fill in
 * a registered_buffer struct.
 * 對(duì)于每一個(gè)使用XLogRegisterBuffer注冊(cè)的每個(gè)數(shù)據(jù)塊,
 *   填充到registered_buffer結(jié)構(gòu)體中
 */
typedef struct
{
    //slot是否在使用?
    bool        in_use;         /* is this slot in use? */
    //REGBUF_* 相關(guān)標(biāo)記
    uint8       flags;          /* REGBUF_* flags */
    //定義關(guān)系和數(shù)據(jù)庫的標(biāo)識(shí)符
    RelFileNode rnode;          /* identifies the relation and block */
    //fork進(jìn)程編號(hào)
    ForkNumber  forkno;
    //塊編號(hào)
    BlockNumber block;
    //頁內(nèi)容
    Page        page;           /* page content */
    //rdata鏈中的數(shù)據(jù)總大小
    uint32      rdata_len;      /* total length of data in rdata chain */
    //使用該數(shù)據(jù)塊注冊(cè)的數(shù)據(jù)鏈頭
    XLogRecData *rdata_head;    /* head of the chain of data registered with
                                 * this block */
    //使用該數(shù)據(jù)塊注冊(cè)的數(shù)據(jù)鏈尾
    XLogRecData *rdata_tail;    /* last entry in the chain, or &rdata_head if
                                 * empty */
    //臨時(shí)rdatas數(shù)據(jù)引用,用于存儲(chǔ)XLogRecordAssemble()中使用的備份塊數(shù)據(jù)
    XLogRecData bkp_rdatas[2];  /* temporary rdatas used to hold references to
                                 * backup block data in XLogRecordAssemble() */

    /* buffer to store a compressed version of backup block image */
    //用于存儲(chǔ)壓縮版本的備份塊鏡像的緩存
    char        compressed_page[PGLZ_MAX_BLCKSZ];
} registered_buffer;
//registered_buffer指針(全局變量)
static registered_buffer *registered_buffers;
//已分配的大小
static int  max_registered_buffers; /* allocated size */
//最大塊號(hào) + 1(當(dāng)前注冊(cè)塊)
static int  max_registered_block_id = 0;    /* highest block_id + 1 currently
                                             * registered */

rmid(Resource Manager ID)的定義
0 XLOG
1 Transaction
2 Storage
3 CLOG
4 Database
5 Tablespace
6 MultiXact
7 RelMap
8 Standby
9 Heap2
10 Heap
11 Btree
12 Hash
13 Gin
14 Gist
15 Sequence
16 SPGist

二、源碼解讀

XLogRecordAssemble函數(shù)從已注冊(cè)的數(shù)據(jù)和緩沖區(qū)中組裝XLOG record到XLogRecData鏈中,組裝完成后可以使用XLogInsertRecord()函數(shù)插入到WAL buffer中.
詳見上一小節(jié)

三、跟蹤分析

場(chǎng)景二:在數(shù)據(jù)表中插入第n條記錄
測(cè)試腳本如下:

testdb=# drop table t_wal_normal;
DROP TABLE
testdb=# create table t_wal_normal(c1 int not null,c2  varchar(40),c3 varchar(40));
CREATE TABLE
testdb=# insert into t_wal_normal(c1,c2,c3) select i,'C2-'||i,'C3-'||i from generate_series(1,8191) as i;
INSERT 0 8191
testdb=# select pg_size_pretty(pg_relation_size('t_wal_normal'));
 pg_size_pretty 
----------------
 424 kB
(1 row)
testdb=# checkpoint;
CHECKPOINT
testdb=# insert into t_wal_normal(c1,c2,c3) VALUES(8192,'C2-8192','C3-8192');

設(shè)置斷點(diǎn),進(jìn)入XLogRecordAssemble

(gdb) b XLogRecordAssemble
Breakpoint 1 at 0x565411: file xloginsert.c, line 488.
(gdb) c
Continuing.

Breakpoint 1, XLogRecordAssemble (rmid=10 '\n', info=0 '\000', RedoRecPtr=5509173376, doPageWrites=true, 
    fpw_lsn=0x7ffd7eb51408) at xloginsert.c:488
488   uint32    total_len = 0;

輸入?yún)?shù):
rmid=10即0x0A --> Heap
info=0 '\000'
RedoRecPtr=5509173376,十六進(jìn)制值為0x00000001485F5080
doPageWrites=true,需要full-page-write
fpw_lsn=0x7ffd7eb51408(fpw是full-page-write的簡(jiǎn)稱,注意該變量仍未賦值)
接下來是變量賦值.
hdr_scratch的定義為:static char *hdr_scratch = NULL;
hdr_rdt的定義為:static XLogRecData hdr_rdt;

(gdb) n
491   registered_buffer *prev_regbuf = NULL;
(gdb) 
494   char     *scratch = hdr_scratch;
(gdb) 
502   rechdr = (XLogRecord *) scratch;
(gdb) 
503   scratch += SizeOfXLogRecord;
(gdb)  p *(XLogRecord *)rechdr
$1 = {xl_tot_len = 0, xl_xid = 0, xl_prev = 0, xl_info = 0 '\000', xl_rmid = 0 '\000', xl_crc = 0}
(gdb) p hdr_scratch
$2 = 0x1f6a4c0 ""
(gdb) 

配置hdr_rdt(用于存儲(chǔ)header信息)

(gdb) n
505   hdr_rdt.next = NULL;
(gdb) 
506   rdt_datas_last = &hdr_rdt;
(gdb) n
507   hdr_rdt.data = hdr_scratch;
(gdb) 
515   if (wal_consistency_checking[rmid])

full-page-write's LSN初始化

(gdb) n
523   *fpw_lsn = InvalidXLogRecPtr;
(gdb) 
524   for (block_id = 0; block_id < max_registered_block_id; block_id++)
(gdb) p max_registered_block_id
$7 = 1

開始循環(huán),獲取regbuf.
regbuf是使用

(gdb) n
526     registered_buffer *regbuf = &registered_buffers[block_id];
(gdb) 
531     XLogRecordBlockCompressHeader cbimg = {0};
(gdb) 
533     bool    is_compressed = false;
(gdb) p *regbuf
$3 = {in_use = true, flags = 8 '\b', rnode = {spcNode = 1663, dbNode = 16402, relNode = 25271}, forkno = MAIN_FORKNUM, 
  block = 52, page = 0x7f391f91b380 "\001", rdata_len = 26, rdata_head = 0x1f6a2c0, rdata_tail = 0x1f6a2d8, bkp_rdatas = {{
      next = 0x0, data = 0x0, len = 0}, {next = 0x0, data = 0x0, len = 0}}, compressed_page = '\000' <repeats 8195 times>}
(gdb)
################
testdb=# select oid,relname,reltablespace from pg_class where relfilenode = 25271;
  oid  |   relname    | reltablespace 
-------+--------------+---------------
 25271 | t_wal_normal |             0
(1 row)
##############

注意:
在內(nèi)存中,main data已由函數(shù)XLogRegisterData注冊(cè),由mainrdata_head和mainrdata_last指針維護(hù),本例中,填充了xl_heap_insert結(jié)構(gòu)體.
block data由XLogRegisterBuffer初始化,通過XLogRegisterBufData注冊(cè)(填充)數(shù)據(jù),分別是xl_heap_header結(jié)構(gòu)體和實(shí)際數(shù)據(jù)(實(shí)質(zhì)上是char *指針,最終填充的數(shù)據(jù),由組裝器確定).

1.main data

(gdb) p *mainrdata_head
$4 = {next = 0x0, data = 0x7ffd7eb51480 "\r", len = 3}
(gdb) p *(xl_heap_insert *)regbuf->rdata_head->data
$8 = {offnum = 3, flags = 2 '\002'}
(gdb) 

2.block data -> xl_heap_header

(gdb) p *regbuf->rdata_head
$6 = {next = 0x1f6a2d8, data = 0x7ffd7eb51470 "\003", len = 5}
(gdb)  p *(xl_heap_header *)regbuf->rdata_head->data
$7 = {t_infomask2 = 3, t_infomask = 2050, t_hoff = 24 '\030'}
(gdb) 

3.block data -> tuple data

(gdb) p *regbuf->rdata_head->next
$9 = {next = 0x0, data = 0x1feb07f "", len = 21}

查看地址0x1feb07f開始的21個(gè)字節(jié)數(shù)據(jù),指向?qū)嶋H的數(shù)據(jù).
第一個(gè)字段:首字節(jié)0 '\000'是類型標(biāo)識(shí)符(INT,注:未最終確認(rèn)),后續(xù)4個(gè)字節(jié)是32bit整型0x00002000,即整數(shù)8192.
第二個(gè)字段:首字節(jié) 17 '\021'是類型標(biāo)識(shí)符(VARCHAR,注:未最終確認(rèn)),后續(xù)是實(shí)際數(shù)據(jù),即C2-8192
第三個(gè)字段:與第二個(gè)字段類似(C3-8192)

(gdb) x/21bc 0x1feb07f
0x1feb07f:  0 '\000'  0 '\000'  32 ' '  0 '\000'  0 '\000'  17 '\021' 67 'C'  50 '2'
0x1feb087:  45 '-'  56 '8'  49 '1'  57 '9'  50 '2'  17 '\021' 67 'C'  51 '3'
0x1feb08f:  45 '-'  56 '8'  49 '1'  57 '9'  50 '2'
(gdb) 

4.block data -> backup block image
這部分內(nèi)容由XLogRecordAssemble()函數(shù)填充.

繼續(xù)執(zhí)行后續(xù)邏輯,regbuf->flags=0x08表示REGBUF_STANDARD(標(biāo)準(zhǔn)的page layout)

(gdb) n
536     if (!regbuf->in_use)
(gdb) 
540     if (regbuf->flags & REGBUF_FORCE_IMAGE)
(gdb) p regbuf->flags
$10 = 8 '\b'

doPageWrites = T,需要執(zhí)行full-page-write.
page_lsn = 5509173336,十六進(jìn)制值為0x00000001485F5058,邏輯ID為0x00000001,物理ID為0x00000048,segment file文文件名稱為00000001 00000001 00000048(時(shí)間線為0x00000001),文件內(nèi)偏移為0x5F5058.

(gdb) n
542     else if (regbuf->flags & REGBUF_NO_IMAGE)
(gdb) 
544     else if (!doPageWrites)
(gdb) 
553       XLogRecPtr  page_lsn = PageGetLSN(regbuf->page);
(gdb) 
555       needs_backup = (page_lsn <= RedoRecPtr);
(gdb) p page_lsn
$11 = 5510687552
(gdb) p RedoRecPtr
$12 = 5510687592

需要執(zhí)行full-page-write

(gdb) n
556       if (!needs_backup)
(gdb) p needs_backup
$20 = true
(gdb) 

判斷是否需要tuple data(needs_data標(biāo)記)

(gdb) n
564     if (regbuf->rdata_len == 0)
(gdb) n
566     else if ((regbuf->flags & REGBUF_KEEP_DATA) != 0)
(gdb) 
569       needs_data = !needs_backup;
(gdb) 
571     bkpb.id = block_id;
(gdb) p needs_data
$14 = false
(gdb) 

配置BlockHeader

(gdb) n
572     bkpb.fork_flags = regbuf->forkno;
(gdb) 
573     bkpb.data_length = 0;
(gdb) 
575     if ((regbuf->flags & REGBUF_WILL_INIT) == REGBUF_WILL_INIT)
(gdb) 
582     include_image = needs_backup || (info & XLR_CHECK_CONSISTENCY) != 0;
(gdb) 
584     if (include_image)
(gdb) p bkpb
$15 = {id = 0 '\000', fork_flags = 0 '\000', data_length = 0}
(gdb) p include_image
$16 = true
(gdb) 

要求包含塊鏡像(FPI),獲取相應(yīng)的page(64bit的指針)

(gdb) n
586       Page    page = regbuf->page;
(gdb) 
587       uint16    compressed_len = 0;
(gdb) 
593       if (regbuf->flags & REGBUF_STANDARD)
(gdb) p page
$17 = (Page) 0x7f391f91b380 "\001"

查看page內(nèi)容

(gdb) x/8192bx 0x7f391f91b380
0x7f391f91b380: 0x01  0x00  0x00  0x00  0x40  0x6b  0x76  0x48
0x7f391f91b388: 0x00  0x00  0x00  0x00  0x4c  0x00  0x90  0x1d
0x7f391f91b390: 0x00  0x20  0x04  0x20  0x00  0x00  0x00  0x00
0x7f391f91b398: 0xd0  0x9f  0x58  0x00  0xa0  0x9f  0x58  0x00
0x7f391f91b3a0: 0x70  0x9f  0x58  0x00  0x40  0x9f  0x58  0x00
0x7f391f91b3a8: 0x10  0x9f  0x58  0x00  0xe0  0x9e  0x58  0x00
0x7f391f91b3b0: 0xb0  0x9e  0x58  0x00  0x80  0x9e  0x58  0x00
0x7f391f91b3b8: 0x50  0x9e  0x58  0x00  0x20  0x9e  0x58  0x00
0x7f391f91b3c0: 0xf0  0x9d  0x58  0x00  0xc0  0x9d  0x58  0x00
0x7f391f91b3c8: 0x90  0x9d  0x58  0x00  0x00  0x00  0x00  0x00
0x7f391f91b3d0: 0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00
0x7f391f91b3d8: 0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00
0x7f391f91b3e0: 0x00  0x00  0x00  0x00  0x00  0x00  0x00  0x00  --> 頭部數(shù)據(jù)
...
0x7f391f91d330: 0x02  0x00  0x03  0x00  0x02  0x09  0x18  0x00
0x7f391f91d338: 0xf5  0x1f  0x00  0x00  0x11  0x43  0x32  0x2d
0x7f391f91d340: 0x38  0x31  0x38  0x31  0x11  0x43  0x33  0x2d
0x7f391f91d348: 0x38  0x31  0x38  0x31  0x00  0x00  0x00  0x00
0x7f391f91d350: 0xe7  0x07  0x00  0x00  0x00  0x00  0x00  0x00
0x7f391f91d358: 0x00  0x00  0x00  0x00  0x00  0x00  0x34  0x00
0x7f391f91d360: 0x01  0x00  0x03  0x00  0x02  0x09  0x18  0x00
0x7f391f91d368: 0xf4  0x1f  0x00  0x00  0x11  0x43  0x32  0x2d
0x7f391f91d370: 0x38  0x31  0x38  0x30  0x11  0x43  0x33  0x2d
0x7f391f91d378: 0x38  0x31  0x38  0x30  0x00  0x00  0x00  0x00 --> 部分尾部數(shù)據(jù)

換用字符格式查看0x7f391f91d330開始的數(shù)據(jù)

(gdb) x/80bc 0x7f391f91d330
0x7f391f91d330: 2 '\002'  0 '\000'  3 '\003'  0 '\000'  2 '\002'  9 '\t'  24 '\030' 0 '\000'
0x7f391f91d338: -11 '\365'  31 '\037' 0 '\000'  0 '\000'  17 '\021' 67 'C'  50 '2'  45 '-'
0x7f391f91d340: 56 '8'  49 '1'  56 '8'  49 '1'  17 '\021' 67 'C'  51 '3'  45 '-'
0x7f391f91d348: 56 '8'  49 '1'  56 '8'  49 '1'  0 '\000'  0 '\000'  0 '\000'  0 '\000'
0x7f391f91d350: -25 '\347'  7 '\a'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'
0x7f391f91d358: 0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  0 '\000'  52 '4'  0 '\000'
0x7f391f91d360: 1 '\001'  0 '\000'  3 '\003'  0 '\000'  2 '\002'  9 '\t'  24 '\030' 0 '\000'
0x7f391f91d368: -12 '\364'  31 '\037' 0 '\000'  0 '\000'  17 '\021' 67 'C'  50 '2'  45 '-'
0x7f391f91d370: 56 '8'  49 '1'  56 '8'  48 '0'  17 '\021' 67 'C'  51 '3'  45 '-'
0x7f391f91d378: 56 '8'  49 '1'  56 '8'  48 '0'  0 '\000'  0 '\000'  0 '\000'  0 '\000'
(gdb) 

標(biāo)準(zhǔn)塊(REGBUF_STANDARD),獲取lower&upper,并設(shè)置hole信息

(gdb) n
596         uint16    lower = ((PageHeader) page)->pd_lower;
(gdb) 
597         uint16    upper = ((PageHeader) page)->pd_upper;
(gdb) 
599         if (lower >= SizeOfPageHeaderData &&
(gdb) p lower
$18 = 76
(gdb) p upper
$19 = 7568
(gdb) n
600           upper > lower &&
(gdb) 
603           bimg.hole_offset = lower;
(gdb) 
604           cbimg.hole_length = upper - lower;
(gdb) n
623       if (wal_compression)
(gdb) p bimg
$22 = {length = 49648, hole_offset = 76, bimg_info = 0 '\000'}
(gdb) p cbimg
$23 = {hole_length = 7492}
(gdb) 

沒有啟用壓縮,則不嘗試壓縮block

(gdb) p wal_compression
$24 = false
(gdb) 

設(shè)置標(biāo)記BKPBLOCK_HAS_IMAGE

(gdb) n
636       bkpb.fork_flags |= BKPBLOCK_HAS_IMAGE;
(gdb) 
641       rdt_datas_last->next = &regbuf->bkp_rdatas[0];
(gdb) 

設(shè)置相關(guān)信息

(gdb) 
641       rdt_datas_last->next = &regbuf->bkp_rdatas[0];
(gdb) n
642       rdt_datas_last = rdt_datas_last->next;
(gdb) 
644       bimg.bimg_info = (cbimg.hole_length == 0) ? 0 : BKPIMAGE_HAS_HOLE;
(gdb) 
652       if (needs_backup)

設(shè)置標(biāo)記BKPIMAGE_APPLY

(gdb) n
653         bimg.bimg_info |= BKPIMAGE_APPLY;
(gdb) 
655       if (is_compressed)
(gdb) p bimg
$26 = {length = 49648, hole_offset = 76, bimg_info = 5 '\005'}

沒有壓縮存儲(chǔ),設(shè)置相關(guān)信息

(gdb) p is_compressed
$27 = false
(gdb) n
665         bimg.length = BLCKSZ - cbimg.hole_length;
(gdb) 
667         if (cbimg.hole_length == 0)
(gdb) 
(gdb) p bimg
$28 = {length = 700, hole_offset = 76, bimg_info = 5 '\005'}

設(shè)置rdt_datas_last變量,構(gòu)建hdr_rdt鏈表,分別指向hole前面部分hole后面部分

675           rdt_datas_last->data = page;
(gdb) 
676           rdt_datas_last->len = bimg.hole_offset;
(gdb) 
678           rdt_datas_last->next = &regbuf->bkp_rdatas[1];
(gdb) 
679           rdt_datas_last = rdt_datas_last->next;
(gdb) 
682             page + (bimg.hole_offset + cbimg.hole_length);
(gdb) 
681           rdt_datas_last->data =
(gdb) 
684             BLCKSZ - (bimg.hole_offset + cbimg.hole_length);
(gdb) 
683           rdt_datas_last->len =
(gdb) 
688       total_len += bimg.length;

查看hdr_rdt相關(guān)信息

(gdb) p hdr_rdt
$31 = {next = 0x1f6c218, data = 0x1f6a4c0 "", len = 0}
(gdb) p *hdr_rdt->next
$34 = {next = 0x1f6c230, data = 0x7f391f91b380 "\001", len = 76}
(gdb) p *hdr_rdt->next->next
$35 = {next = 0x0, data = 0x7f391f91d110 "\351\a", len = 624}
(gdb) 

設(shè)置大小

(gdb) n
691     if (needs_data)
(gdb) p total_len
$36 = 700
(gdb) 

不需要處理tuple data,不是同一個(gè)REL

(gdb) p needs_data
$37 = false
(gdb) 
(gdb) n
705     if (prev_regbuf && RelFileNodeEquals(regbuf->rnode, prev_regbuf->rnode))
(gdb) p prev_regbuf
$38 = (registered_buffer *) 0x0
(gdb) n
711       samerel = false;
(gdb) 
712     prev_regbuf = regbuf;
(gdb) 

拷貝頭部信息到scratch緩沖區(qū)中

(gdb) 
715     memcpy(scratch, &bkpb, SizeOfXLogRecordBlockHeader);
(gdb) 
716     scratch += SizeOfXLogRecordBlockHeader;
(gdb) 

包含F(xiàn)PI,追加SizeOfXLogRecordBlockImageHeader

(gdb) n
717     if (include_image)
(gdb) 
719       memcpy(scratch, &bimg, SizeOfXLogRecordBlockImageHeader);
(gdb) 
720       scratch += SizeOfXLogRecordBlockImageHeader;
(gdb) 
721       if (cbimg.hole_length != 0 && is_compressed)
(gdb) 
728     if (!samerel)
(gdb) 

不是同一個(gè)REL,追加RelFileNode

728     if (!samerel)
(gdb) 
730       memcpy(scratch, &regbuf->rnode, sizeof(RelFileNode));
(gdb) 
731       scratch += sizeof(RelFileNode);
(gdb) 

后跟blocknumber

733     memcpy(scratch, &regbuf->block, sizeof(BlockNumber));
(gdb) 
734     scratch += sizeof(BlockNumber);

繼續(xù)下一個(gè)block

(gdb) 
524   for (block_id = 0; block_id < max_registered_block_id; block_id++)
(gdb) 

已處理完畢,接下來,是XLOG Record origin(不需要)

(gdb) n
738   if ((curinsert_flags & XLOG_INCLUDE_ORIGIN) &&
(gdb) 
739     replorigin_session_origin != InvalidRepOriginId)
(gdb) 
738   if ((curinsert_flags & XLOG_INCLUDE_ORIGIN) &&
(gdb) 
747   if (mainrdata_len > 0)
(gdb) 

接下來是main data,使用XLR_BLOCK_ID_DATA_SHORT格式

(gdb) n
749     if (mainrdata_len > 255)
(gdb) 
757       *(scratch++) = (char) XLR_BLOCK_ID_DATA_SHORT;
(gdb) p mainrdata_len
$39 = 3
(gdb) 

調(diào)整rdt_datas_last變量信息,調(diào)整總大小

(gdb) n
758       *(scratch++) = (uint8) mainrdata_len;
(gdb) 
760     rdt_datas_last->next = mainrdata_head;
(gdb) 
761     rdt_datas_last = mainrdata_last;
(gdb) 
762     total_len += mainrdata_len;
(gdb) 
764   rdt_datas_last->next = NULL;
(gdb) p total_len
$40 = 703
(gdb) 

調(diào)整hdr_rdt信息

(gdb) n
766   hdr_rdt.len = (scratch - hdr_scratch);
(gdb) 
767   total_len += hdr_rdt.len;
(gdb) 
777   INIT_CRC32C(rdata_crc);
(gdb) p hdr_rdt
$41 = {next = 0x1f6c218, data = 0x1f6a4c0 "", len = 51}
(gdb) p total_len
$42 = 754
(gdb) 

計(jì)算CRC

(gdb) 
779   for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next)
(gdb) 
780     COMP_CRC32C(rdata_crc, rdt->data, rdt->len);
(gdb) 
779   for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next)
(gdb) 
780     COMP_CRC32C(rdata_crc, rdt->data, rdt->len);
(gdb) 
779   for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next)
(gdb) 
780     COMP_CRC32C(rdata_crc, rdt->data, rdt->len);
(gdb) 
779   for (rdt = hdr_rdt.next; rdt != NULL; rdt = rdt->next)
(gdb) 
787   rechdr->xl_xid = GetCurrentTransactionIdIfAny();
(gdb) 

填充記錄頭部信息的其他域字段.

(gdb) n
788   rechdr->xl_tot_len = total_len;
(gdb) 
789   rechdr->xl_info = info;
(gdb) 
790   rechdr->xl_rmid = rmid;
(gdb) 
791   rechdr->xl_prev = InvalidXLogRecPtr;
(gdb) 
792   rechdr->xl_crc = rdata_crc;
(gdb) 
794   return &hdr_rdt;
(gdb) p *(XLogRecord *)rechdr
$43 = {xl_tot_len = 754, xl_xid = 2025, xl_prev = 0, xl_info = 0 '\000', xl_rmid = 10 '\n', xl_crc = 1615792998}
(gdb) 

返回hdr_rdt

(gdb) n
795 }
(gdb) 
XLogInsert (rmid=10 '\n', info=0 '\000') at xloginsert.c:462
462     EndPos = XLogInsertRecord(rdt, fpw_lsn, curinsert_flags);
(gdb) 
(gdb) p *rdt
$44 = {next = 0x1f6c218, data = 0x1f6a4c0 "\362\002", len = 51}
(gdb) p fpw_lsn
$45 = 0
(gdb) p curinsert_flags
$46 = 1 '\001'
(gdb) 

DONE!

四、參考資料

Write Ahead Logging — WAL
PostgreSQL 源碼解讀(4)- 插入數(shù)據(jù)#3(heap_insert)
PostgreSQL 事務(wù)日志W(wǎng)AL結(jié)構(gòu)淺析
PostgreSQL 源碼解讀(110)- WAL#6(Insert&WAL - XLogRecordAssemble記錄組裝函數(shù))
PG Source Code

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI