話說啟動(dòng)進(jìn)程調(diào)用StartupXLOG啟動(dòng)xlog,根據(jù)情況,如果需要就排除系統(tǒng)故障引起的數(shù)據(jù)庫不一致狀態(tài),做相應(yīng)的REDO或UNDO,然后創(chuàng)建一個(gè)檢查點(diǎn),把所有共享內(nèi)存磁盤緩沖和提交數(shù)據(jù)緩沖寫并文件同步到磁盤、把檢查點(diǎn)插入xlog文件、更新控制文件,使數(shù)據(jù)庫達(dá)到一種狀態(tài) 。
這節(jié)接著討論啟動(dòng)進(jìn)程在創(chuàng)建檢查點(diǎn)時(shí)調(diào)用的 CheckPointGuts 方法(在創(chuàng)建重啟點(diǎn)時(shí)也會(huì)調(diào)用這個(gè)方法)。 CheckPointGuts 方法功能是刷出所有共享內(nèi)存中的數(shù)據(jù)到磁盤并做文件同步,共享內(nèi)存中的數(shù)據(jù)包括 clog 、 subtrans 、 multixact 、 predicate 、 relationmap 、 buffer (數(shù)據(jù)文件)和 twophase 相關(guān)數(shù)據(jù)。 CheckPointGuts 方法定義和“ CheckPointGuts 方法調(diào)用序列圖 ”見下面。
static void
CheckPointGuts(XLogRecPtrcheckPointRedo, int flags)
{
CheckPointCLOG();
CheckPointSUBTRANS();
CheckPointMultiXact();
CheckPointPredicate();
CheckPointRelationMap();
CheckPointBuffers(flags); /* performs all required fsyncs */
/* We deliberately delay 2PC checkpointingas long as possible */
CheckPointTwoPhase(checkPointRedo);
}
CheckPointGuts 方法調(diào)用序列圖
CheckPointGuts 方法主要是通過調(diào)用提交事務(wù)日志管理器的方法 CheckPointClog ,子事務(wù)日志管理器的方法 CheckPointSUBTRANS ,多事務(wù)日志管理器的方法 CheckPointMultiXact ,支持序列化事務(wù)隔離級(jí)別的謂詞鎖模塊的方法 CheckPointPredicate ,目錄 / 系統(tǒng)表到文件節(jié)點(diǎn)映射模塊的方法 CheckPointRelationMap ,緩存管理器的方法 CheckPointBuffers ,兩階段提交模塊的方法 CheckPointTwoPhase 把共享內(nèi)存里的數(shù)據(jù)刷出并文件同步到磁盤。
其中 提交事務(wù)日志管理器的方法 CheckPointClog 、 子事務(wù)日志管理器的方法 CheckPointSUBTRANS 、多事務(wù)日志管理器的方法 CheckPointMultiXact 、多事務(wù)日志管理器的方法 CheckPointMultiXact 、支持序列化事務(wù)隔離級(jí)別的謂詞鎖模塊的方法 CheckPointPredicate 最后都調(diào)用了 SLRU 模塊的 SimpleLruFlush 方法,把相關(guān)共享內(nèi)存數(shù)據(jù)寫到磁盤,并調(diào)用 pg_fsync 方法把相關(guān)內(nèi)容文件同步到磁盤上對(duì)應(yīng)文件。
在緩存管理器的方法 CheckPointBuffers ,兩階段提交模塊的方法 CheckPointTwoPhase 里,因?yàn)闆]有使用 SLRU 算法,直接調(diào)用 pg_fsync 方法把相關(guān)內(nèi)容文件同步到磁盤上對(duì)應(yīng)文件。
在目錄 / 系統(tǒng)表到文件節(jié)點(diǎn)映射模塊的方法 CheckPointRelationMap 里,在釋放 RelationMappingLock 時(shí),會(huì)完成共享內(nèi)存里相關(guān)系統(tǒng)表和對(duì)應(yīng)物理文件映射的文件同步到磁盤工作。
我們看一下各種日志管理,日志對(duì)數(shù)據(jù)庫是至關(guān)重要的一部分,出現(xiàn)系統(tǒng)故障時(shí),數(shù)據(jù)庫通過重放日志恢復(fù)數(shù)據(jù),保證數(shù)據(jù)庫一致性和完整性。
Pg 里有 XLOG 、 CLOG 、 SUBTRANS LOG 、 MultiXactID LOG 四種事務(wù)日志, XLOG 是事務(wù)日志,就是平時(shí)常說的 REDOLOG ,記錄了事務(wù)操作數(shù)據(jù)庫的過程信息和事務(wù)最終狀態(tài); CLOG 是 XLOG 里事務(wù)的提交狀態(tài)日志; SUBTRANS 是子事務(wù)日志, 為每一個(gè)事務(wù)存儲(chǔ)父事務(wù)ID。這是嵌套事務(wù)實(shí)現(xiàn)的基礎(chǔ)部分, SUBTRANS 僅需要為當(dāng)前打開的事務(wù)記住信息,沒有必要在崩潰并重啟后保留數(shù)據(jù); MultiXactID 是組合事務(wù)日志,由一組事務(wù) ID 組成, 是共享行鎖 shared-row-lock 實(shí)現(xiàn)的基礎(chǔ)部分,共享鎖鎖住的元組在其Xmax字段存儲(chǔ)MultiXactId。各種日志都存放在對(duì)應(yīng)的日志文件里。
有了文件就有了I/O,為了降低I/O開銷,pg設(shè)置了各種日志的緩存區(qū),由對(duì)應(yīng)的日志管理器管理日志的寫、文件同步和讀等日志維護(hù)工作。Pg使用簡(jiǎn)單最近最少使用(SLRU)算法來管理事務(wù)日志。使用輕量鎖LWLock的 ControlLock 鎖保護(hù)整個(gè)緩沖區(qū),其中的每個(gè)緩沖塊(默認(rèn)8K)還有一個(gè)LWLock鎖保護(hù),以控制并發(fā)操作。SLRU及事務(wù)日志的部分相關(guān)數(shù)據(jù)結(jié)構(gòu)在下面。
為CLOG控制鏈接到共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)
static SlruCtlData ClogCtlData;
#define ClogCtl (&ClogCtlData)
為SUBTRANS控制鏈接到共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)
static SlruCtlData SubTransCtlData;
#define SubTransCtl (&SubTransCtlData)
為MultiXact控制鏈接到共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)
static SlruCtlData MultiXactOffsetCtlData;
static SlruCtlData MultiXactMemberCtlData;
#define MultiXactOffsetCtl (&MultiXactOffsetCtlData)
#define MultiXactMemberCtl (&MultiXactMemberCtlData)
typedef SlruCtlData * SlruCtl ;
/* SlruCtlData 是指向共享內(nèi)存里的活躍信息的非共享結(jié)構(gòu) */
typedef struct SlruCtlData
{
SlruShared shared ;
/* 這個(gè)標(biāo)志告訴 是否文件同步寫( pg_clog 和 multixact 成員是 true,pg_subtrans 和 pg_notify 是 false ) */
bool do_fsync ;
/* 為截?cái)嗄康臎Q定兩個(gè)頁號(hào)哪一個(gè)是更舊的。為了用 包裹 XID 算法( with wraparound XID arithmetic )做正確的事,這兒我們需要用事務(wù) ID 比較 */
bool (* PagePrecedes ) ( int , int );
/* 在 SimpleLruInit 期間目錄被設(shè)置,并且從那以后不變。因?yàn)樗偸窍嗤模槐胤诺焦蚕韮?nèi)存里。 */
char Dir [64];
} SlruCtlData ;
共享內(nèi)存狀態(tài)
typedef struct SlruSharedData
{
LWLockId ControlLock ;
/* 由這個(gè) SLRU 結(jié)構(gòu)管理的緩存塊號(hào) */
int num_slots ;
/* 持有每一個(gè)緩存槽信息的數(shù)組。當(dāng)狀態(tài)是 EMPTY 時(shí) 緩存頁 / 塊號(hào)是未定義的,當(dāng)作
page_lru_count 。 */
char ** page_buffer ;
SlruPageStatus * page_status ;
bool * page_dirty ;
int * page_number ;
int * page_lru_count ;
LWLockId * buffer_locks ;
/* 在 SLRU 頁 / 塊里的相關(guān)條目的 WAL 刷出 LSN 的可選數(shù)組。如果不是 0/NULL ,在寫緩存頁 / 塊前 我們必須刷出 WAL ( pg_clog 是 true , multixact 、 pg_subtrans 、 pg_notify 是 false )。 Group_lsn[] 每緩存頁 / 塊槽有 lsn_groups_per_page 條目,在這個(gè)槽的緩存頁 / 塊上 為 SLRU 條目的一個(gè)臨近組 每一個(gè)緩存頁 / 塊槽 包含最高已知 LSN 。 */
XLogRecPtr * group_lsn ;
int lsn_groups_per_page ;
/* 我們通過設(shè)置 page_lru_count[ slotno ] = ++cur_lru_count 標(biāo)記頁“最近使用”;最老舊頁因此是有表達(dá)式 cur_lru_count - page_lru_count[ slotno ] 值最高 / 大的那一個(gè)。這個(gè)數(shù)事實(shí)上包裹,但這個(gè)計(jì)算仍然工作 和緩存頁 / 塊的年齡(超過了 INT_MAX 數(shù))一樣長(zhǎng)。 */
int cur_lru_count ;
/* latest_page_number 是當(dāng)前日志結(jié)尾的頁 / 塊號(hào);這不是嚴(yán)格的數(shù)據(jù),因?yàn)槲覀儍H用它避免包裹 swapping 出了最后的頁 / 塊。 */
int latest_page_number ;
} SlruSharedData ;
typedef SlruSharedData * SlruShared ;
/* 頁狀態(tài)代碼。注意這不包含 "dirty" 位。僅在 VALID 或者 WRIT_IN_PROGRESS 狀態(tài)里 page_dirty 能是 true ;在后面的例子 / 情況里 它暗示 從這次寫開始后頁又被搞臟 */
typedef enum
{
SLRU_PAGE_EMPTY , /* buffer is not in use */
SLRU_PAGE_READ_IN_PROGRESS , /* page is beingread in */
SLRU_PAGE_VALID , /* page is valid and not being written */
SLRU_PAGE_WRITE_IN_PROGRESS /* page is beingwritten out */
} SlruPageStatus ;
SLRU 算法的緩存區(qū)操作在下面,其中包括了本節(jié)多次調(diào)用的 SimpleLruFlush 方法,將緩存數(shù)據(jù)刷出并文件同步到磁盤。
extern Size SimpleLruShmemSize ( int nslots, int nlsns);
extern void SimpleLruInit ( SlruCtl ctl, const char *name, int nslots, int nlsns,
LWLockId ctllock, const char *subdir);
extern int SimpleLruZeroPage ( SlruCtl ctl, int pageno);
extern int SimpleLruReadPage ( SlruCtl ctl, int pageno, bool write_ok,
TransactionId xid);
extern int SimpleLruReadPage_ReadOnly ( SlruCtl ctl, int pageno,
TransactionId xid);
extern void SimpleLruWritePage ( SlruCtl ctl, int slotno);
extern void SimpleLruFlush ( SlruCtl ctl, bool checkpoint);
extern void SimpleLruTruncate ( SlruCtl ctl, int cutoffPage);
extern bool SlruScanDirectory ( SlruCtl ctl, int cutoffPage, bool doDeletions);
就到這兒吧。
------------
轉(zhuǎn)載請(qǐng)著明出處,來自博客:
blog.csdn.net/beiigang
beigang.iteye.com
PostgreSQL啟動(dòng)過程中的那些事十六:?jiǎn)?dòng)進(jìn)程三:CheckPointGuts刷出共享內(nèi)存里所有數(shù)據(jù)
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
