标簽
PostgreSQL , checkpoint , standby , CreateRestartPoint
https://github.com/digoal/blog/blob/master/201806/20180601_02.md#%E8%83%8C%E6%99%AF 背景
當資料庫異常關閉時,資料庫關閉時來不及或根本沒有機會建立一個一緻性的檢查點,是以需要從上一個一緻性檢查點開始恢複。
實際上是資料庫啟動時檢查控制檔案中的資料庫狀态,如果狀态不是shutdown的,那麼說明資料庫是異常關閉的(當然我們說,除了recovery狀态),總之就需要從檢查點開始恢複。
src/include/catalog/pg_control.h
/*
* System status indicator. Note this is stored in pg_control; if you change
* it, you must bump PG_CONTROL_VERSION
*/
typedef enum DBState
{
DB_STARTUP = 0,
DB_SHUTDOWNED,
DB_SHUTDOWNED_IN_RECOVERY,
DB_SHUTDOWNING,
DB_IN_CRASH_RECOVERY,
DB_IN_ARCHIVE_RECOVERY,
DB_IN_PRODUCTION
} DBState;
那麼問題來了,對于standby,一直是出于recovery狀态的,即使停庫也可能是這個狀态(實際上是shutdown_in_recovery),是以STANDBY停庫再起來,理論上來說又需要重上一個檢查點開始恢複WAL,即使這個資料庫已經恢複到了最新的WAL。
為了提高性能,縮短recovery時間,pg在standby上面也支援了checkpoint,叫做CreateRestartPoint。
CreateRestartPoint@src/backend/access/transam/xlog.c
/*
* Establish a restartpoint if possible.
*
* This is similar to CreateCheckPoint, but is used during WAL recovery
* to establish a point from which recovery can roll forward without
* replaying the entire recovery log.
*
* Returns true if a new restartpoint was established. We can only establish
* a restartpoint if we have replayed a safe checkpoint record since last
* restartpoint.
*/
bool
CreateRestartPoint(int flags)
{
當standby關閉,重新開機時,從restartpoint開始APPLY WAL,而不是上遊節點(主節點)在WAL日志中寫的檢查點開始。
停庫時,備庫寫檢查點
/*
* This must be called ONCE during postmaster or standalone-backend shutdown
*/
void
ShutdownXLOG(int code, Datum arg)
{
/* Don't be chatty in standalone mode */
ereport(IsPostmasterEnvironment ? LOG : NOTICE,
(errmsg("shutting down")));
/*
* 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()) // 備庫寫檢查點
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();
}
CreateRestartPoint
...
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
if (ControlFile->state == DB_IN_ARCHIVE_RECOVERY &&
ControlFile->checkPointCopy.redo < lastCheckPoint.redo)
{
ControlFile->checkPoint = lastCheckPointRecPtr;
ControlFile->checkPointCopy = lastCheckPoint;
ControlFile->time = (pg_time_t) time(NULL);
/*
* Ensure minRecoveryPoint is past the checkpoint record. Normally,
* this will have happened already while writing out dirty buffers,
* but not necessarily - e.g. because no buffers were dirtied. We do
* this because a non-exclusive base backup uses minRecoveryPoint to
* determine which WAL files must be included in the backup, and the
* file (or files) containing the checkpoint record must be included,
* at a minimum. Note that for an ordinary restart of recovery there's
* no value in having the minimum recovery point any earlier than this
* anyway, because redo will begin just after the checkpoint record.
*/
if (ControlFile->minRecoveryPoint < lastCheckPointEndPtr)
{
ControlFile->minRecoveryPoint = lastCheckPointEndPtr;
ControlFile->minRecoveryPointTLI = lastCheckPoint.ThisTimeLineID;
/* update local copy */
minRecoveryPoint = ControlFile->minRecoveryPoint;
minRecoveryPointTLI = ControlFile->minRecoveryPointTLI;
}
if (flags & CHECKPOINT_IS_SHUTDOWN)
ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY; // 備庫正常關閉時,狀态改寫為DB_SHUTDOWNED_IN_RECOVERY
UpdateControlFile();
}
https://github.com/digoal/blog/blob/master/201806/20180601_02.md#%E5%B0%8F%E7%BB%93 小結
PostgreSQL備庫也可以寫檢查點,目的是避免每次重新開機備庫都需要從上一個檢查點(由主庫産生,在WAL中回放出來的)APPLY後面所有的WAL。
進而forward 備庫停庫後重新開機時WAL的位點。