来看这个:
pg_time_t time; /* time stamp of last pg_control update */
当初初始化的时候,是这样的:
/* * This func must be called ONCE on system install. It creates pg_control * and the initial XLOG segment. */ void BootStrapXLOG(void) { ... CheckPoint checkPoint; ... checkPoint.time = (pg_time_t) time(NULL); ... ControlFile->time = checkPoint.time; }
之后、启动时:
/* * This must be called ONCE during postmaster or standalone-backend startup */ void StartupXLOG(void) { … /* REDO */ if (InRecovery) { … ControlFile->time = (pg_time_t) time(NULL); … UpdateControlFile(); … } … ControlFile->time = (pg_time_t) time(NULL); UpdateControlFile(); … }
Shutdown的时候:
/* * Perform a checkpoint --- either during shutdown, or on-the-fly * * flags is a bitwise OR of the following: * CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown. * CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery. * CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP, * ignoring checkpoint_completion_target parameter. * CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occured * since the last one (implied by CHECKPOINT_IS_SHUTDOWN or * CHECKPOINT_END_OF_RECOVERY). * * Note: flags contains other bits, of interest here only for logging purposes. * In particular note that this routine is synchronous and does not pay * attention to CHECKPOINT_WAIT. */ void CreateCheckPoint(int flags) { … if (shutdown) { LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); ControlFile->state = DB_SHUTDOWNING; ControlFile->time = (pg_time_t) time(NULL); UpdateControlFile(); LWLockRelease(ControlFileLock); } … /* * Update the control file. */ LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); if (shutdown) ControlFile->state = DB_SHUTDOWNED; ControlFile->prevCheckPoint = ControlFile->checkPoint; ControlFile->checkPoint = ProcLastRecPtr; ControlFile->checkPointCopy = checkPoint; ControlFile->time = (pg_time_t) time(NULL); /* crash recovery should always recover to the end of WAL */ MemSet(&ControlFile->minRecoveryPoint, 0, sizeof(XLogRecPtr)); UpdateControlFile(); LWLockRelease(ControlFileLock); … }
还有一种特别的情况:那就是 ReStartPoint:
/* * 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) { … /* * If the last checkpoint record we've replayed is already our last * restartpoint, we can't perform a new restart point. We still update * minRecoveryPoint in that case, so that if this is a shutdown restart * point, we won't start up earlier than before. That's not strictly * necessary, but when hot standby is enabled, it would be rather weird if * the database opened up for read-only connections at a point-in-time * before the last shutdown. Such time travel is still possible in case of * immediate shutdown, though. * * We don't explicitly advance minRecoveryPoint when we do create a * restartpoint. It's assumed that flushing the buffers will do that as a * side-effect. */ if (XLogRecPtrIsInvalid(lastCheckPointRecPtr) || XLByteLE(lastCheckPoint.redo, ControlFile->checkPointCopy.redo)) { XLogRecPtr InvalidXLogRecPtr = {0, 0}; ereport(DEBUG2, (errmsg("skipping restartpoint, already performed at %X/%X", lastCheckPoint.redo.xlogid, lastCheckPoint.redo.xrecoff))); 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; } … /* * Update pg_control, using current time. Check that it still shows * IN_ARCHIVE_RECOVERY state and an older checkpoint, else do nothing; * this is a quick hack to make sure nothing really bad happens if somehow * we get here after the end-of-recovery checkpoint. */ LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); if (ControlFile->state == DB_IN_ARCHIVE_RECOVERY && XLByteLT(ControlFile->checkPointCopy.redo, lastCheckPoint.redo)) { ControlFile->prevCheckPoint = ControlFile->checkPoint; ControlFile->checkPoint = lastCheckPointRecPtr; ControlFile->checkPointCopy = lastCheckPoint; ControlFile->time = (pg_time_t) time(NULL); if (flags & CHECKPOINT_IS_SHUTDOWN) ControlFile->state = DB_SHUTDOWNED_IN_RECOVERY; UpdateControlFile(); } … }
那么,何时会触发 RestartPoint呢?
http://www.postgresql.org/docs/9.1/static/wal-configuration.html
Checkpoints are points in the sequence of transactions at which it is guaranteed that the heap and index data files have been updated with all information written before the checkpoint. At checkpoint time, all dirty data pages are flushed to disk and a special checkpoint record is written to the log file. (The changes were previously flushed to the WAL files.) In the event of a crash, the crash recovery procedure looks at the latest checkpoint record to determine the point in the log (known as the redo record) from which it should start the REDO operation. Any changes made to data files before that point are guaranteed to be already on disk. Hence, after a checkpoint, log segments preceding the one containing the redo record are no longer needed and can be recycled or removed. (When WAL archiving is being done, the log segments must be archived before being recycled or removed.)
The checkpoint requirement of flushing all dirty data pages to disk can cause a significant I/O load. For this reason, checkpoint activity is throttled so I/O begins at checkpoint start and completes before the next checkpoint starts; this minimizes performance degradation during checkpoints.
The server's background writer process automatically performs a checkpoint every so often. A checkpoint is created every checkpoint_segments log segments, or every checkpoint_timeout seconds, whichever comes first. The default settings are 3 segments and 300 seconds (5 minutes), respectively. It is also possible to force a checkpoint by using the SQL command CHECKPOINT.
上面是说 checkpoint。再来看restartpoint:
其实是为了防止recovery动作里再次出错:
In archive recovery or standby mode, the server periodically performs restartpoints which are similar to checkpoints in normal operation: the server forces all its state to disk, updates the pg_control file to indicate that the already-processed WAL data need not be scanned again, and then recycles any old log segment files in pg_xlog directory. A restartpoint is triggered if at least one checkpoint record has been replayed and checkpoint_timeout seconds have passed since last restartpoint. In standby mode, a restartpoint is also triggered if checkpoint_segments log segments have been replayed since last restartpoint and at least one checkpoint record has been replayed. Restartpoints can't be performed more frequently than checkpoints in the master because restartpoints can only be performed at checkpoint records.
其运行的前提是:archive recovery 或者 standby mode,这都是要用到 archive recovery的。
再看相关代码:
头文件:
/* * OR-able request flag bits for checkpoints. The "cause" bits are used only * for logging purposes. Note: the flags must be defined so that it's * sensible to OR together request flags arising from different requestors. */ /* These directly affect the behavior of CreateCheckPoint and subsidiaries */ #define CHECKPOINT_IS_SHUTDOWN 0x0001 /* Checkpoint is for shutdown */ #define CHECKPOINT_END_OF_RECOVERY 0x0002 /* Like shutdown checkpoint, #define CHECKPOINT_IMMEDIATE 0x0004 /* Do it without delays */ #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 */ #define CHECKPOINT_CAUSE_XLOG 0x0020 /* XLOG consumption */ #define CHECKPOINT_CAUSE_TIME 0x0040 /* Elapsed time */
shutdown时,要判断是否是在recovery的时候进行的,从而设置一个特殊的DB_SHUTDOWNED_IN_RECOVERY状态:
/* * This must be called ONCE during postmaster or standalone-backend shutdown */ void ShutdownXLOG(int code, Datum arg) { ereport(LOG, (errmsg("shutting down"))); 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(); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); } ShutdownCLOG(); ShutdownSUBTRANS(); ShutdownMultiXact(); ereport(LOG, (errmsg("database system is shut down"))); }
还有就是 bgwriter也要进行调用:
/* * Main entry point for bgwriter process * * This is invoked from BootstrapMain, which has already created the basic * execution environment, but not enabled signals yet. */ void BackgroundWriterMain(void) { … /* * Loop forever */ for (;;) { bool do_checkpoint = false; int flags = 0; … if (checkpoint_requested) { checkpoint_requested = false; do_checkpoint = true; BgWriterStats.m_requested_checkpoints++; } … /* * Force a checkpoint if too much time has elapsed since the last one. * Note that we count a timed checkpoint in stats only when this * occurs without an external request, but we set the CAUSE_TIME flag * bit even if there is also an external request. */ now = (pg_time_t) time(NULL); elapsed_secs = now - last_checkpoint_time; if (elapsed_secs >= CheckPointTimeout) { if (!do_checkpoint) BgWriterStats.m_timed_checkpoints++; do_checkpoint = true; flags |= CHECKPOINT_CAUSE_TIME; } /* * Do a checkpoint if requested, otherwise do one cycle of * dirty-buffer writing. */ if (do_checkpoint) { bool ckpt_performed = false; bool do_restartpoint; … /* * Check if we should perform a checkpoint or a restartpoint. As a * side-effect, RecoveryInProgress() initializes TimeLineID if * it's not set yet. */ do_restartpoint = RecoveryInProgress(); … /* * The end-of-recovery checkpoint is a real checkpoint that's * performed while we're still in recovery. */ if (flags & CHECKPOINT_END_OF_RECOVERY) do_restartpoint = false; … /* * Do the checkpoint. */ if (!do_restartpoint) { CreateCheckPoint(flags); ckpt_performed = true; } else ckpt_performed = CreateRestartPoint(flags); … } else BgBufferSync(); … } }