話說啟動進程調用StartupXLOG啟動xlog,根據情況,如果需要就排除系統故障引起的數據庫不一致狀態,做相應的REDO或UNDO,然后創建一個檢查點,把所有共享內存磁盤緩沖和提交數據緩沖寫并文件同步到磁盤、把檢查點插入xlog文件、更新控制文件,使數據庫達到一種狀態 。
這節接著討論啟動進程在創建檢查點時調用的 CheckPointGuts 方法(在創建重啟點時也會調用這個方法)。 CheckPointGuts 方法功能是刷出所有共享內存中的數據到磁盤并做文件同步,共享內存中的數據包括 clog 、 subtrans 、 multixact 、 predicate 、 relationmap 、 buffer (數據文件)和 twophase 相關數據。 CheckPointGuts 方法定義和“ CheckPointGuts 方法調用序列圖 ”見下面。
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 方法調用序列圖
CheckPointGuts 方法主要是通過調用提交事務日志管理器的方法 CheckPointClog ,子事務日志管理器的方法 CheckPointSUBTRANS ,多事務日志管理器的方法 CheckPointMultiXact ,支持序列化事務隔離級別的謂詞鎖模塊的方法 CheckPointPredicate ,目錄 / 系統表到文件節點映射模塊的方法 CheckPointRelationMap ,緩存管理器的方法 CheckPointBuffers ,兩階段提交模塊的方法 CheckPointTwoPhase 把共享內存里的數據刷出并文件同步到磁盤。
其中 提交事務日志管理器的方法 CheckPointClog 、 子事務日志管理器的方法 CheckPointSUBTRANS 、多事務日志管理器的方法 CheckPointMultiXact 、多事務日志管理器的方法 CheckPointMultiXact 、支持序列化事務隔離級別的謂詞鎖模塊的方法 CheckPointPredicate 最后都調用了 SLRU 模塊的 SimpleLruFlush 方法,把相關共享內存數據寫到磁盤,并調用 pg_fsync 方法把相關內容文件同步到磁盤上對應文件。
在緩存管理器的方法 CheckPointBuffers ,兩階段提交模塊的方法 CheckPointTwoPhase 里,因為沒有使用 SLRU 算法,直接調用 pg_fsync 方法把相關內容文件同步到磁盤上對應文件。
在目錄 / 系統表到文件節點映射模塊的方法 CheckPointRelationMap 里,在釋放 RelationMappingLock 時,會完成共享內存里相關系統表和對應物理文件映射的文件同步到磁盤工作。
我們看一下各種日志管理,日志對數據庫是至關重要的一部分,出現系統故障時,數據庫通過重放日志恢復數據,保證數據庫一致性和完整性。
Pg 里有 XLOG 、 CLOG 、 SUBTRANS LOG 、 MultiXactID LOG 四種事務日志, XLOG 是事務日志,就是平時常說的 REDOLOG ,記錄了事務操作數據庫的過程信息和事務最終狀態; CLOG 是 XLOG 里事務的提交狀態日志; SUBTRANS 是子事務日志, 為每一個事務存儲父事務ID。這是嵌套事務實現的基礎部分, SUBTRANS 僅需要為當前打開的事務記住信息,沒有必要在崩潰并重啟后保留數據; MultiXactID 是組合事務日志,由一組事務 ID 組成, 是共享行鎖 shared-row-lock 實現的基礎部分,共享鎖鎖住的元組在其Xmax字段存儲MultiXactId。各種日志都存放在對應的日志文件里。
有了文件就有了I/O,為了降低I/O開銷,pg設置了各種日志的緩存區,由對應的日志管理器管理日志的寫、文件同步和讀等日志維護工作。Pg使用簡單最近最少使用(SLRU)算法來管理事務日志。使用輕量鎖LWLock的 ControlLock 鎖保護整個緩沖區,其中的每個緩沖塊(默認8K)還有一個LWLock鎖保護,以控制并發操作。SLRU及事務日志的部分相關數據結構在下面。
為CLOG控制鏈接到共享內存數據結構
static SlruCtlData ClogCtlData;
#define ClogCtl (&ClogCtlData)
為SUBTRANS控制鏈接到共享內存數據結構
static SlruCtlData SubTransCtlData;
#define SubTransCtl (&SubTransCtlData)
為MultiXact控制鏈接到共享內存數據結構
static SlruCtlData MultiXactOffsetCtlData;
static SlruCtlData MultiXactMemberCtlData;
#define MultiXactOffsetCtl (&MultiXactOffsetCtlData)
#define MultiXactMemberCtl (&MultiXactMemberCtlData)
typedef SlruCtlData * SlruCtl ;
/* SlruCtlData 是指向共享內存里的活躍信息的非共享結構 */
typedef struct SlruCtlData
{
SlruShared shared ;
/* 這個標志告訴 是否文件同步寫( pg_clog 和 multixact 成員是 true,pg_subtrans 和 pg_notify 是 false ) */
bool do_fsync ;
/* 為截斷目的決定兩個頁號哪一個是更舊的。為了用 包裹 XID 算法( with wraparound XID arithmetic )做正確的事,這兒我們需要用事務 ID 比較 */
bool (* PagePrecedes ) ( int , int );
/* 在 SimpleLruInit 期間目錄被設置,并且從那以后不變。因為它總是相同的,它不必放到共享內存里。 */
char Dir [64];
} SlruCtlData ;
共享內存狀態
typedef struct SlruSharedData
{
LWLockId ControlLock ;
/* 由這個 SLRU 結構管理的緩存塊號 */
int num_slots ;
/* 持有每一個緩存槽信息的數組。當狀態是 EMPTY 時 緩存頁 / 塊號是未定義的,當作
page_lru_count 。 */
char ** page_buffer ;
SlruPageStatus * page_status ;
bool * page_dirty ;
int * page_number ;
int * page_lru_count ;
LWLockId * buffer_locks ;
/* 在 SLRU 頁 / 塊里的相關條目的 WAL 刷出 LSN 的可選數組。如果不是 0/NULL ,在寫緩存頁 / 塊前 我們必須刷出 WAL ( pg_clog 是 true , multixact 、 pg_subtrans 、 pg_notify 是 false )。 Group_lsn[] 每緩存頁 / 塊槽有 lsn_groups_per_page 條目,在這個槽的緩存頁 / 塊上 為 SLRU 條目的一個臨近組 每一個緩存頁 / 塊槽 包含最高已知 LSN 。 */
XLogRecPtr * group_lsn ;
int lsn_groups_per_page ;
/* 我們通過設置 page_lru_count[ slotno ] = ++cur_lru_count 標記頁“最近使用”;最老舊頁因此是有表達式 cur_lru_count - page_lru_count[ slotno ] 值最高 / 大的那一個。這個數事實上包裹,但這個計算仍然工作 和緩存頁 / 塊的年齡(超過了 INT_MAX 數)一樣長。 */
int cur_lru_count ;
/* latest_page_number 是當前日志結尾的頁 / 塊號;這不是嚴格的數據,因為我們僅用它避免包裹 swapping 出了最后的頁 / 塊。 */
int latest_page_number ;
} SlruSharedData ;
typedef SlruSharedData * SlruShared ;
/* 頁狀態代碼。注意這不包含 "dirty" 位。僅在 VALID 或者 WRIT_IN_PROGRESS 狀態里 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 算法的緩存區操作在下面,其中包括了本節多次調用的 SimpleLruFlush 方法,將緩存數據刷出并文件同步到磁盤。
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);
就到這兒吧。
------------
轉載請著明出處,來自博客:
blog.csdn.net/beiigang
beigang.iteye.com
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

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