數據庫異常關閉時,數據庫關閉時來不及或者沒機會作checkpoint,則須要從上一個一致性檢查的開始恢復。數據庫
PostgreSQL備機checkpoint是不能產生checkpoint WAL的,由於若是寫這樣類型的checkpoint的話,就會將接收的WAL打亂,那麼日誌將混亂,回放會出問題。ide
那麼問題來了,備機支持checkpoint嗎?他的checkpoint怎麼作的?函數
PostgreSQL爲了縮短恢復時間,備機上也支持checkpoint,即CreateRestartPoint。可是其pg_control文件的checkpoint記錄的位點是從主機傳過來WAL裏面的checkpoint記錄位置。post
一、備機回放ui
StartupXLOG do{ ... RmgrTable[record->xl_rmid].rm_redo(xlogreader);//回放 ... record = ReadRecord(xlogreader, InvalidXLogRecPtr, LOG, false);//讀取一個xlog } while (record != NULL);
二、回放函數日誌
void xlog_redo(XLogReaderState *record) { ... else if (info == XLOG_CHECKPOINT_SHUTDOWN){ ... memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); ... RecoveryRestartPoint(&checkPoint); }else if (info == XLOG_CHECKPOINT_ONLINE){ ... memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint)); ... RecoveryRestartPoint(&checkPoint); } ... }
三、RecoveryRestartPointcode
static void RecoveryRestartPoint(const CheckPoint *checkPoint) { ... SpinLockAcquire(&XLogCtl->info_lck); XLogCtl->lastCheckPointRecPtr = ReadRecPtr;//ReadRecPtr爲讀取checkpoint記錄後的位置 XLogCtl->lastCheckPointEndPtr = EndRecPtr; XLogCtl->lastCheckPoint = *checkPoint; SpinLockRelease(&XLogCtl->info_lck); }
四、ReadRecPtr賦值orm
ReadRecord for (;;) { char *errormsg; record = XLogReadRecord(xlogreader, RecPtr, &errormsg); ReadRecPtr = xlogreader->ReadRecPtr; EndRecPtr = xlogreader->EndRecPtr; ... }
五、備機createcheckpointrem
bool CreateRestartPoint(int flags) { LWLockAcquire(CheckpointLock, LW_EXCLUSIVE); /* Get a local copy of the last safe checkpoint record. */ SpinLockAcquire(&XLogCtl->info_lck); lastCheckPointRecPtr = XLogCtl->lastCheckPointRecPtr;//checkpoint的位置來自XLogCtl->lastCheckPointRecPtr lastCheckPointEndPtr = XLogCtl->lastCheckPointEndPtr; lastCheckPoint = XLogCtl->lastCheckPoint; SpinLockRelease(&XLogCtl->info_lck); ... if (XLogRecPtrIsInvalid(lastCheckPointRecPtr) || lastCheckPoint.redo <= ControlFile->checkPointCopy.redo){ //回放了最後一個checkpoint記錄後,備機再次手動執行checkpoint命令 UpdateMinRecoveryPoint(InvalidXLogRecPtr, true); if (flags & CHECKPOINT_IS_SHUTDOWN){ LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY; ControlFile->time = (pg_time_t) time(NULL); UpdateControlFile(); LWLockRelease(ControlFileLock); } LWLockRelease(CheckpointLock); return false; } ... LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); if (ControlFile->state == DB_IN_ARCHIVE_RECOVERY && ControlFile->checkPointCopy.redo < lastCheckPoint.redo){ ControlFile->prevCheckPoint = ControlFile->checkPoint; ControlFile->checkPoint = lastCheckPointRecPtr;//checkpoint的位置 ControlFile->checkPointCopy = lastCheckPoint; ControlFile->time = (pg_time_t) time(NULL); ... if (flags & CHECKPOINT_IS_SHUTDOWN) ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY; UpdateControlFile(); } ... return true; }
六、備機shutdownit
void ShutdownXLOG(int code, Datum arg) { /* * Signal walsenders to move to stopping state. */ WalSndInitStopping(); /* * Wait for WAL senders to be in stopping state. This prevents commands * from writing new WAL. */ WalSndWaitStopping(); if (RecoveryInProgress())//備機寫checkpoint CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); else { /* * If archiving is enabled, rotate the last XLOG file so that all the * remaining records are archived (postmaster wakes up the archiver * process one more time at the end of shutdown). The checkpoint * record will go to the next XLOG file and won't be archived (yet). */ if (XLogArchivingActive() && XLogArchiveCommandSet()) RequestXLogSwitch(false); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); } ShutdownCLOG(); ShutdownCommitTs(); ShutdownSUBTRANS(); ShutdownMultiXact(); }
七、總結
PostgreSQL備庫也能夠寫檢查點,目的是避免每次重啓備庫都須要從上一個檢查點(由主庫產生,在WAL中回放出來的)APPLY後面全部的WAL。可是他記錄的checkpoint位點是從主庫傳過來的。這樣的話就有問題了,若是主機很長時間都沒有作checkpoint了,備機即便正常關閉,重啓時,也會從上一個checkpoint開始恢復,這樣也會恢復很長時間;而且屢次重啓也須要從上一次checkpoint開始重複恢復。