天天看點

PostgreSQL standby會不會做檢查點? 以及做檢查點的用處

标簽

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的位點。

https://github.com/digoal/blog/blob/master/201806/20180601_02.md#%E5%8F%82%E8%80%83 參考