xlog日誌header部分詳解
前些天,做了些實驗,也確實能夠證明手動同步xlog數據是可行性的,爲了更深入的研究,我覺得研究下xlog的源碼,並且打開xlog的二進制文件,讀一讀它裏面的內容。
本文來自:http://blog.csdn.net/lengzijian/article/details/7836402
首先來看下xlog日誌的結構圖:
從上到下觀察下:
xlog日誌文件命名:
文件名 = 時間線 + 日誌文件號(logid) + 段號(segmented)
分爲三部分,每個部分由8個16進制字符組成,時間線由1開始,日誌文件號和段號由0開始,所以在系統中第一個事務日誌文件名爲:000000010000000000000000
一個xlog文件日誌備有256個段組成,每個段大小爲16M,當最近的文件名有000000010000000000000000變爲000000010000000100000000時,實際增加了256個段文件,但是邏輯上只增加了一個xlog日誌文件。
每一個段文件又分爲很多頁面,每個一面的大小爲一個塊的大小:
include/pg_config.h:
#defineXLOG_BLCKSZ 8192 //塊大小
//頁面個數 = 16M / 8192bit = 2048
對於每一個日誌頁面,需要在器頭部寫一個頭部信息XLogPageHeaderData,數據結構如下:
include/access/xlog_internal.h
typedef struct XLogPageHeaderData { uint16 xlp_magic; /* 校驗位 */ uint16 xlp_info; /* 標誌位 */ TimeLineID xlp_tli; /* 頁面第一條記錄的時間序列 */ XLogRecPtr xlp_pageaddr; /* XLOG 頁面首地址 */ } XLogPageHeaderData; |
其中標誌位xlp_info只使用最低兩位“01”或者“10”或者11:
“01”表明該頁的第一個XLOG記錄着上一個頁的最後一個XLOG記錄,這樣的記錄會接在一個XLogContRecord結構體之後;
“10”表明該頁的頭部是一個長頭部,既該頁是XLOG段文件的首頁;
這樣頭部標誌位會有三種狀態:
/* 如果記錄跨越兩個頁面,這隻該標誌位到新的頁面 */ #define XLP_FIRST_IS_CONTRECORD 0x0001 /*這裏就是之前說過的表示該頁是xlog段文件的首頁 */ #define XLP_LONG_HEADER 0x0002 /* 表示既是首頁也是contrecore*/ #define XLP_ALL_FLAGS 0x0003 |
長頭部結構體如下:
typedef struct XLogLongPageHeaderData { XLogPageHeaderData std; /* 頭部信息 */ uint64 xlp_sysid; /* pg_control中的系統標識符 */ uint32 xlp_seg_size; /* 校驗位,段的尺寸*/ uint32 xlp_xlog_blcksz; /* 校驗位,塊的尺寸 */ } XLogLongPageHeaderData; |
要注意的地方,對於一條很長的xlog日誌,當頁面沒有足夠的空間存儲時,postgreseql允許把多餘的數據存儲到下一個頁面,即同一條日誌寫到了兩個文件檔中。一種數據除外,頭部數據是不允許存到兩個頁面當中的。
當出現一條日誌存放到兩個頁面時,需要用XLogContRecord來記錄該頁除了本頁面存儲的內容外,剩餘的存儲數據信息:
typedef struct XLogContRecord { uint32 xl_rem_len; /* 記錄中剩餘信息的總長度*/ } XLogContRecord; |
使用xlogdump(之後做一篇詳細介紹xlogdump的文章)工具來觀察下頁面頭信息(也可以觀察源碼xlogdump.c文件的readXLogPage函數),之後詳細講解xlogdump用法:
打印信息如下:
[page:0, xlp_info:3, xlp_tli:1, xlp_pageaddr:0/4C000000] XLP_FIRST_IS_CONTRECORD XLP_LONG_HEADER #第一頁 |
函數方法爲:
static bool readXLogPage(void) { size_t nread = read(logFd, pageBuffer, XLOG_BLCKSZ);
if (nread == XLOG_BLCKSZ) { logPageOff += XLOG_BLCKSZ; if (((XLogPageHeader) pageBuffer)->xlp_magic != XLOG_PAGE_MAGIC) { printf("Bogus page magic number %04X at offset %X\n", ((XLogPageHeader) pageBuffer)->xlp_magic, logPageOff); }
/* * FIXME: check xlp_magic here. */ if (!enable_stats) { printf("[page:%d, xlp_info:%d, xlp_tli:%d, xlp_pageaddr:%X/%X] ", logPageOff / XLOG_BLCKSZ, ((XLogPageHeader) pageBuffer)->xlp_info, ((XLogPageHeader) pageBuffer)->xlp_tli, ((XLogPageHeader) pageBuffer)->xlp_pageaddr.xlogid, ((XLogPageHeader) pageBuffer)->xlp_pageaddr.xrecoff);
if ( (((XLogPageHeader)pageBuffer)->xlp_info & XLP_FIRST_IS_CONTRECORD) ) printf("XLP_FIRST_IS_CONTRECORD "); if ((((XLogPageHeader)pageBuffer)->xlp_info & XLP_LONG_HEADER) ) printf("XLP_LONG_HEADER "); #if PG_VERSION_NUM >= 90200 if ((((XLogPageHeader)pageBuffer)->xlp_info & XLP_BKP_REMOVABLE) ) printf("XLP_BKP_REMOVABLE "); #endif
printf("\n"); }
return true; } if (nread != 0) { fprintf(stderr, "Partial page of %d bytes ignored\n", (int) nread); } return false; } |