PostgreSQL啟動過程中的那些事十六:啟動程序二
這節主要討論啟動程序到了StartupXLOG。根據情況,如果需要就排除系統故障引起的資料庫不一致狀態,做相應的REDO或UNDO,然後建立一個檢查點,把所有共享記憶體磁碟緩衝和提交資料緩衝寫並檔案同步到磁碟、把檢查點插入xlog檔案、更新控制檔案,使資料庫達到一種狀態,設定共享記憶體中XLogCtl、ShmemVariableCache等物件資訊;如果不需要,就根據控制檔案從xlog檔案讀取最後的檢查點資訊,設定共享記憶體中XLogCtl、ShmemVariableCache等物件資訊;啟動完XLOG,啟動程序完成使命,自己做了了斷,postmaster程序根據子程序結束訊號響應控制代碼繼續。
目前沒有看到資料檔案裡記錄了檢查點,難道這個沒有???
3
先上個圖方法呼叫序列示意圖
4
上StartupXLog方法的處理流程示意圖
StartupXLog流程示意圖資料庫在什麼情況下需要恢復。如果出現事務故障、系統故障或者介質故障時,資料庫需要恢復。出現諸如運算溢位、死鎖、違反完整性約束等事務故障時資料庫在執行時可以通過強制回滾自行處理。系統故障是主存資料丟失,未完成事務有些資料已寫到物理資料庫,此時資料庫啟動時需要事務回滾(UNDO/rollback)恢復。已完成事務有些或全部資料未寫到物理資料庫,此時資料庫啟動時需要重做(REDO)已提交事務。介質故障,需要從以前的備份做專門恢復。
話說到了XLogStartup方法,在這個方法裡最主要的是根據情況判斷是否需要恢復。如果不需要恢復,處理比較簡單;如果需要恢復,根據不同狀況做相應恢復。這兒這個狀況就是判斷系統是否在上次關閉時出現了系統故障。
XLogStartup方法,先讀取控制檔案pg_control到ControlFileData資料結構,再看是否有恢復命令檔案recovery.conf(判斷是否是歸檔模式,如果歸檔模式,需要恢復的話,就是歸檔恢復),讀取內容並設定InArchiveRecovery = true,並根據情況,如果是hot standby的從系統,設定StandbyMode = true。接著讀時間線歷史檔案。
如果不需要恢復,更新控制檔案的state等於DB_IN_PRODUCTION,time等於系統當前時間。接著設定共享記憶體裡的XLogCtl的成員Write.lastSegSwitchTime為當前時間,根據控制檔案初始化XLogCtl的最後的檢查點的XID/epoch,再初始化共享記憶體裡快取變數結構ShmemVariableCache的latestCompletedXid以備份事務ID(ShmemVariableCache->latestCompletedXid = ShmemVariableCache->nextXid)然後呼叫RecoverPreparedTransactions()掃描pg_twophase資料夾重新為已準備好的事務載入共享記憶體狀態。如果任何關鍵GUC引數改變了,在我們允許backend程序寫WAL日誌以前記錄到日誌。所有這些事搞定後,設定xlogctl->SharedRecoveryInProgress= false允許backend程序寫WAL日誌。然後退出啟動程序,postmaster程序響應子程序退出訊號其它相關程序。
如果需要恢復。這兒要處理的是崩潰時刻未完成事務已寫入物理資料庫的事務(處理方法是UNDO)和崩潰時刻已完成事務未寫入物理資料庫的事務(處理方法是REDO)。
根據資料庫的執行模式(有無起歸檔,有無hot standby。有hot standby時,其主系統恢復和啟歸檔情況一樣進行恢復,從系統單獨處理),呼叫ReadRecord方法從不同地方讀取xlog日誌記錄,呼叫xlog的資源管理器xmgr的相應資源的重放方法做恢復。恢復完成後剩餘步驟和不需要恢復的情況一樣,處理後續事宜。然後退出啟動程序,postmaster程序響應子程序退出訊號其它相關程序。略詳細過程見“XLogStartup流程示意圖”
相關主要結構見下面:
控制檔案的結構ControlFileData及檢查點結構CheckPoint參見《PostgreSQL儲存系統一:控制檔案儲存結構》。
XLog日誌檔案相關結構參見《PostgreSQL儲存系統二:REDOLOG檔案儲存結構》。
VariableCache是共享記憶體裡用來跟蹤OID和XID分配狀態的資料結構。由於歷史原因,由不同的輕量鎖LWLock保護這個結構中不同的欄位。
typedefstruct VariableCacheData
{
/*這些欄位由OidGenLock鎖保護 */
OidnextOid; /* next OID to assign */
uint32oidCount; /* OIDs available before must do XLOG work */
/*這些欄位由XidGenLock鎖保護 */
TransactionIdnextXid; /* nextXID to assign */
TransactionIdoldestXid;/* cluster-wideminimum datfrozenxid */
TransactionIdxidVacLimit; /* start forcing autovacuumshere */
TransactionIdxidWarnLimit;/* start complaining here */
TransactionIdxidStopLimit;/* refuse to advance nextXid beyond here */
TransactionIdxidWrapLimit;/* where the world ends */
OidoldestXidDB; /* database with minimum datfrozenxid*/
/*這些欄位由ProcArrayLock鎖保護 */
TransactionIdlatestCompletedXid; /* newest XID that has committed or
* aborted */
}VariableCacheData;
typedefVariableCacheData *VariableCache;
XLOG的共享記憶體總狀態
typedefstruct XLogCtlData
{
/* 由WALInsertLock鎖保護 */
XLogCtlInsertInsert;
/* 由info_lck鎖保護 */
XLogwrtRqstLogwrtRqst;
XLogwrtResultLogwrtResult;
uint32 ckptXidEpoch; /* nextXID & epoch of latest checkpoint*/
TransactionIdckptXid;
XLogRecPtr asyncXactLSN; /* LSN of newest async commit/abort */
uint32 lastRemovedLog; /* latest removed/recycled XLOG segment */
uint32 lastRemovedSeg;
/* 由WALWriteLock鎖保護 */
XLogCtlWriteWrite;
/*儘管這些值可以變,但在啟動後不再改變。是否可以讀/寫頁面和塊的值依賴於WALInsertLock和WALWriteLock鎖 */
char *pages; /* buffers for unwritten XLOG pages */
XLogRecPtr*xlblocks; /* 1st byte ptr-s + XLOG_BLCKSZ */
intXLogCacheBlck; /* highest allocated xlog buffer index */
TimeLineID ThisTimeLineID;
TimeLineID RecoveryTargetTLI;
/* archiveCleanupCommand是從recovery.conf檔案裡讀的,但需要放在共享記憶體裡以使bgwriter程序能訪問它 */
chararchiveCleanupCommand[MAXPGPATH];
/* SharedRecoveryInProgress指明本程序是否正在做崩潰或歸檔恢復。由
info_lck鎖保護 */
bool SharedRecoveryInProgress;
/*
* SharedHotStandbyActive 指明本程序是否正在做崩潰或歸檔恢復。由
info_lck鎖保護 */
bool SharedHotStandbyActive;
/*如果正在等WAL到達或者failover的觸發器檔案出現,recoveryWakeupLatch用於喚醒啟動將成繼續重放WAL。 */
Latch recoveryWakeupLatch;
/*在恢復期間,我們在這兒儲存最後一個檢查點的拷貝。當bgwriter想建立一個重啟點restartpoint時由bgwriter程序使用。由info_lck鎖保護。 */
XLogRecPtr lastCheckPointRecPtr;
CheckPoint lastCheckPoint;
/* 最後一個檢查點或被重放的檢查點的結束位置加1 */
XLogRecPtr replayEndRecPtr;
/*被重放的最後一個記錄的結束位置加1 */
XLogRecPtr recoveryLastRecPtr;
/*最後被重放的 COMMIT/ABORT記錄的時間戳 */
TimestampTzrecoveryLastXTime;
/*是否請求暫停恢復? */
bool recoveryPause;
slock_t info_lck; /* locks shared variables shown above */
} XLogCtlData;
static XLogCtlData *XLogCtl = NULL;
/* XLogInsert的共享狀態資料結構*/
typedefstruct XLogCtlInsert
{
XLogwrtResultLogwrtResult; /* a recent value of LogwrtResult */
XLogRecPtr PrevRecord; /* start of previously-inserted record */
intcurridx; /* current block index in cache */
XLogPageHeadercurrpage; /* points to header of block in cache */
char *currpos; /* current insertion point in cache */
XLogRecPtr RedoRecPtr; /* current redo point for insertions */
bool forcePageWrites; /* forcing full-page writes for PITR? */
/*如果在程序裡備份由pg_start_backup()開始,exclusiveBackup是true;nonExclusiveBackups是計數器,指明程序裡當前基於流備份的數目。當上面兩個任一個非0時(即有上面的備份時),forcePageWrites是ture。lastBackupStart是最後一個檢查點的redo值(下一個xlog記錄的位置指標),作為線上備份的起始點。 */
bool exclusiveBackup;
intnonExclusiveBackups;
XLogRecPtr lastBackupStart;
} XLogCtlInsert;
XLOG控制的共享記憶體資料結構,LogwrtRqst指出我們需要寫/檔案同步到日誌的那個位元組位置(在這個位置之前的所有記錄必須被寫或做檔案同步)。LogwrtResult指出我們已經寫/檔案同步了的位元組位置。
typedefstruct XLogwrtRqst
{
XLogRecPtr Write; /* last byte + 1 to write out */
XLogRecPtr Flush; /* last byte + 1 to flush */
} XLogwrtRqst;
typedefstruct XLogwrtResult
{
XLogRecPtr Write; /* last byte + 1 written out */
XLogRecPtr Flush; /* last byte + 1 flushed */
} XLogwrtResult;
指向XLOG裡位置的指標。這個指標是64位,因為我們不想它有溢位的時候。
注意:用來指明一個無效的指標。這個沒問題,因為我們在XLOG頁頭用了頁頭結構,因此XLOG記錄不可能從頁頭開始。
注意:這兒容易引起理解錯亂,這個xlogid(對應實際XLOG檔名字的中間八位)表示邏輯XLOG日誌檔案ID,因為組成XLOG邏輯檔案的實際物理檔案遠小於4Gb。組成對應這個xlogid的邏輯日誌檔案的每一個實際物理檔案是一個XLogSegSize位元組大小的“段”("segment",段號是實際XLOG檔名字的後八位)。前面加上用八位表示的一個時間線ID、邏輯日誌檔案號和段號一起標識一個物理的XLOG日誌檔案(“段”)。段號和物理檔案裡的偏移量由xrecoff/XLogSegSize和xrecoff%XLogSegSize計算。
typedefstruct XLogRecPtr
{
uint32xlogid; /* log file #, 0 based */
uint32xrecoff; /* byte offset of location in log file */
}XLogRecPtr;
/* XLogWrite/XLogFlush的共享記憶體裡的狀態資料結構 */
typedefstruct XLogCtlWrite
{
XLogwrtResultLogwrtResult; /* current value of LogwrtResult */
intcurridx; /* cache index of next block to write */
pg_time_t lastSegSwitchTime; /* time of last xlog segmentswitch */
} XLogCtlWrite;
/*系統狀態指示器。 */
typedefenum DBState
{
DB_STARTUP = 0,
DB_SHUTDOWNED,
DB_SHUTDOWNED_IN_RECOVERY,
DB_SHUTDOWNING,
DB_IN_CRASH_RECOVERY,
DB_IN_ARCHIVE_RECOVERY,
DB_IN_PRODUCTION
} DBState;
XLogStartup流程示意圖中的兩個紅色方框紅色字的框是XLOG資源管理器xmgr的處理方法,這個XLOG的資源管理器內容較多,單列主題討論。還有恢復完成後呼叫了方法CreateCheckPoint,建立一個檢查點以將所有的恢復資料寫到磁碟。
5 建立檢查點
建立一個檢查點,會將共享記憶體裡的所有磁碟緩衝和提交日誌緩衝刷出並檔案同步到磁碟。
下面這些情況可能引起建立檢查點,為了使用方便,把這些情況定義成如下標誌,這些標誌可以按位做或運算。檢查點的起因不同,建立檢查點的行為也略有不同。
#defineCHECKPOINT_IS_SHUTDOWN 0x0001/* Checkpoint is for shutdown */
#defineCHECKPOINT_END_OF_RECOVERY0x0002 /* Likeshutdown checkpoint,
* but issued at end of WAL
* recovery */
#defineCHECKPOINT_IMMEDIATE 0x0004 /* Do it withoutdelays */
#define CHECKPOINT_FORCE 0x0008 /* Force even if no activity */
/* These are important to RequestCheckpoint */
#define CHECKPOINT_WAIT 0x0010 /* Wait for completion */
/* These indicate the cause of a checkpoint request */
#defineCHECKPOINT_CAUSE_XLOG 0x0020 /* XLOGconsumption */
#define CHECKPOINT_CAUSE_TIME 0x0040 /* Elapsed time */
建立檢查點的基本過程是先讓儲存管理器smgr(以後單列狀態討論)為檢查點做好準備,根據情況填充檢查點結構的成員,CheckpointGuts方法把共享記憶體裡的磁碟緩衝和提交日誌緩衝輸出到磁碟(即寫資料檔案)。接著呼叫XlogInsert把這個檢查點插入xlog檔案。然後更新控制檔案相關成員。最後更新共享記憶體裡XlogCtl的檢查點相關成員和檢查點的統計資訊結構。相關結構定義和建立檢查點流程示意圖見下面。
/*檢查點統計資訊 */
typedefstructCheckpointStatsData
{
TimestampTzckpt_start_t; /* startof checkpoint */
TimestampTzckpt_write_t; /* startof flushing buffers */
TimestampTzckpt_sync_t;/* start of fsyncs*/
TimestampTzckpt_sync_end_t; /* end of fsyncs*/
TimestampTzckpt_end_t; /* end ofcheckpoint */
intckpt_bufs_written; /* # ofbuffers written */
intckpt_segs_added; /* # of new xlog segments created */
intckpt_segs_removed; /* # of xlogsegments deleted */
intckpt_segs_recycled; /* # of xlogsegments recycled */
intckpt_sync_rels;/* # of relations synced */
uint64 ckpt_longest_sync; /* Longest sync for one relation */
uint64 ckpt_agg_sync_time; /* The sum of all the individual sync
* times, which is not necessarily the
* same as the total elapsed time for
* the entire sync phase. */
}CheckpointStatsData;
當前檢查點的統計資訊收集在這個全域性結構變數裡。
CheckpointStatsDataCheckpointStats;
建立檢查點流程示意圖
上圖中,其中CheckPointGuts方法的定義見下面,刷出所有共享記憶體中的資料到磁碟並做檔案同步。方法定義見下面,把clog、subtrans、multixact、predicate、relationmap、buffer(資料檔案)和twophase相關資料統統刷和檔案同步到磁碟。這兒先不深入討論這個方法了。
staticvoid
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);
}
結果這麼多邏輯嚴謹的一系列行為後,資料庫達到了正常狀態,啟動程序壽終正寢。然後,postmaster程序響應該子程序退出,分別依次fork出bgwriter程序、walwriter程序、autovaclauncher程序、archiver程序、pgstat程序,然後丟擲一句”database system is ready to acceptconnections”。然後進入serverloop,等待客戶端請求到達,啟動postgres服務程序,開始履行使命。
Serverloop還檢查bgwriter程序、walwriter程序、autovaclauncher程序、archiver程序、pgstat程序,還有前面啟動的系統日誌程序sysloger這些輔助檢查是否正常執行,如果沒有,就重啟這些程序。此時,pg伺服器端有postmaster程序和這六個輔助程序執行,準備好為客戶端程序提供服務,提供的服務由postgres服務程序完成。
------------
轉載請著明出處,來自部落格:
blog.csdn.net/beiigang
beigang.iteye.com