在执行pg_start_backup函数开启备份模式后,务必要执行pg_stop_backup函数结束备份 (详细参考下方源码)。
结束排他备份
- postgres=# SELECT pg_start_backup('backup02',false,true);
- pg_start_backup
- -----------------
- 0/3000028
- (1 row)
-
- postgres=# SELECT pg_stop_backup(true);
- NOTICE: WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup
- pg_stop_backup
- ----------------
- (0/4000138,,)
- (1 row)
会自动删除backup_label文件
结束非排他备份
- postgres=# SELECT pg_start_backup('pgdata backup',false,false);
- pg_start_backup
- -----------------
- 0/2000060
- (1 row)
-
- postgres=# SELECT pg_stop_backup(false);
- NOTICE: WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup
- pg_stop_backup
- ---------------------------------------------------------------------------
- (0/5000088,"START WAL LOCATION: 0/2000060 (file 000000010000000000000002)+
- CHECKPOINT LOCATION: 0/2000098 +
- BACKUP METHOD: streamed +
- BACKUP FROM: primary +
- START TIME: 2022-07-14 18:39:47 CST +
- LABEL: pgdata backup +
- START TIMELINE: 1 +
- ","")
- (1 row)
在源码中,pg_stop_backup实际对应的是do_pg_stop_backup函数。
从该备份还原时必须存在的最后一个WAL位置,以及*.stoptli_p中相应的时间线ID
- /*
- * do_pg_stop_backup
- *
- * Utility function called at the end of an online backup. It cleans up the backup state and can optionally wait for WAL segments to be archived.
- *
- * Returns the last WAL location that must be present to restore from this backup, and the corresponding timeline ID in *stoptli_p.
- */
- XLogRecPtr
- do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
- {
- bool exclusive = (labelfile == NULL);
- bool backup_started_in_recovery = false;
- XLogRecPtr startpoint;
- XLogRecPtr stoppoint;
- TimeLineID stoptli;
- pg_time_t stamp_time;
- char strfbuf[128];
- char histfilepath[MAXPGPATH];
- char startxlogfilename[MAXFNAMELEN];
- char stopxlogfilename[MAXFNAMELEN];
- char lastxlogfilename[MAXFNAMELEN];
- char histfilename[MAXFNAMELEN];
- char backupfrom[20];
- XLogSegNo _logSegNo;
- FILE *lfp;
- FILE *fp;
- char ch;
- int seconds_before_warning;
- int waits = 0;
- bool reported_waiting = false;
- char *remaining;
- char *ptr;
- uint32 hi,
- lo;
-
- /* 开头的预检查跟start函数类似,这里不再重复介绍 */
- backup_started_in_recovery = RecoveryInProgress();
-
- /*
- * Currently only non-exclusive backup can be taken during recovery.
- */
- if (backup_started_in_recovery && exclusive)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("recovery is in progress"),
- errhint("WAL control functions cannot be executed during recovery.")));
-
- /*
- * During recovery, we don't need to check WAL level. Because, if WAL
- * level is not sufficient, it's impossible to get here during recovery.
- */
- if (!backup_started_in_recovery && !XLogIsNeeded())
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("WAL level not sufficient for making an online backup"),
- errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
-
- if (exclusive)
- {
- /*
- * At first, mark that we're now stopping an exclusive backup, to
- * ensure that there are no other sessions currently running
- * pg_start_backup() or pg_stop_backup().
- */
- WALInsertLockAcquireExclusive();
- if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS)
- {
- WALInsertLockRelease();
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("exclusive backup not in progress")));
- }
- XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING;
- WALInsertLockRelease();
-
- /*
- * Remove backup_label. In case of failure, the state for an exclusive backup is switched back to in-progress.
- * 同样下面代码如果运行失败,会执行回调函数pg_stop_backup_callback,将排他备份状态改回运行中
- */
- PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
- {
- /*
- * Read the existing label file into memory.将标签文件读入内存
- */
- struct stat statbuf;
- int r;
-
- if (stat(BACKUP_LABEL_FILE, &statbuf))
- {
- /* should not happen per the upper checks */
- if (errno != ENOENT)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not stat file \"%s\": %m",
- BACKUP_LABEL_FILE)));
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("a backup is not in progress")));
- }
-
- lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
- if (!lfp)
- {
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not read file \"%s\": %m",
- BACKUP_LABEL_FILE)));
- }
- labelfile = palloc(statbuf.st_size + 1);
- r = fread(labelfile, statbuf.st_size, 1, lfp);
- labelfile[statbuf.st_size] = '\0';
-
- /*
- * Close and remove the backup label file,关闭并删除标签文件
- */
- if (r != 1 || ferror(lfp) || FreeFile(lfp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not read file \"%s\": %m",
- BACKUP_LABEL_FILE)));
- durable_unlink(BACKUP_LABEL_FILE, ERROR);
-
- /*
- * Remove tablespace_map file if present, it is created only if there are tablespaces. 如果表空间映射文件存在,也将其删除
- */
- durable_unlink(TABLESPACE_MAP, DEBUG1);
- }
- PG_END_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
- }
-
- /*
- * OK to update backup counters, forcePageWrites and session-level lock.
- *
- * Note that CHECK_FOR_INTERRUPTS() must not occur while updating them. Otherwise they can be updated inconsistently, and which might cause do_pg_abort_backup() to fail.
- */
- WALInsertLockAcquireExclusive();
- if (exclusive)
- {
- /* 排他模式,修改备份状态为NONE */
- XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE;
- }
- else
- {
- /* 非排他模式,计数器-1 */
- Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
- XLogCtl->Insert.nonExclusiveBackups--;
- }
-
- /* 若当前已无备份(包括排他模式和非排他模式),关闭强制全页写 */
- if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
- XLogCtl->Insert.nonExclusiveBackups == 0)
- {
- XLogCtl->Insert.forcePageWrites = false;
- }
-
- /*
- * Clean up session-level lock.
- * 更新会话级备份状态,并释放插入锁
- */
- sessionBackupState = SESSION_BACKUP_NONE;
-
- WALInsertLockRelease();
-
- /*
- * Read and parse the START WAL LOCATION line,读取并解析labelfile中的START WAL LOCATION行。注意labelfile是非排他模式备份传入的标签文件名,跟前面删除的排他文件生成的backup_label文件不一样。
- */
- if (sscanf(labelfile, "START WAL LOCATION: %X/%X (file %24s)%c",
- &hi, &lo, startxlogfilename,
- &ch) != 4 || ch != '\n')
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
- startpoint = ((uint64) hi) << 32 | lo;
- remaining = strchr(labelfile, '\n') + 1; /* %n is not portable enough */
-
- /*
- * 在剩余内容中解析 BACKUP FROM 行.
- */
- ptr = strstr(remaining, "BACKUP FROM:");
- if (!ptr || sscanf(ptr, "BACKUP FROM: %19s\n", backupfrom) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
- if (strcmp(backupfrom, "standby") == 0 && !backup_started_in_recovery)
- ereport(ERROR,
- (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("the standby was promoted during online backup"),
- errhint("This means that the backup being taken is corrupt "
- "and should not be used. "
- "Try taking another online backup.")));
-
- /* 如果处于恢复阶段,要做一些设置,略 */
- if (backup_started_in_recovery)
- {
- …
- }
- else
- {
- /*
- * Write the backup-end xlog record,写backup-end的XLOG记录
- */
- XLogBeginInsert();
- XLogRegisterData((char *) (&startpoint), sizeof(startpoint));
- stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
- stoptli = ThisTimeLineID;
-
- /*
- * Force a switch to a new xlog segment file, 强制日志切换
- */
- RequestXLogSwitch(false);
-
- XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size);
- XLogFileName(stopxlogfilename, stoptli, _logSegNo, wal_segment_size);
-
- /* Use the log timezone here, not the session timezone */
- stamp_time = (pg_time_t) time(NULL);
- pg_strftime(strfbuf, sizeof(strfbuf),
- "%Y-%m-%d %H:%M:%S %Z",
- pg_localtime(&stamp_time, log_timezone));
-
- /*
- * Write the backup history file,写备份历史文件
- */
- XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
- BackupHistoryFilePath(histfilepath, stoptli, _logSegNo,
- startpoint, wal_segment_size);
- fp = AllocateFile(histfilepath, "w");
- if (!fp)
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not create file \"%s\": %m",
- histfilepath)));
-
- fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n",
- LSN_FORMAT_ARGS(startpoint), startxlogfilename);
- fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n",
- LSN_FORMAT_ARGS(stoppoint), stopxlogfilename);
-
- /*
- * Transfer remaining lines including label and start timeline to history file.
- */
- fprintf(fp, "%s", remaining);
- fprintf(fp, "STOP TIME: %s\n", strfbuf);
- fprintf(fp, "STOP TIMELINE: %u\n", stoptli);
- if (fflush(fp) || ferror(fp) || FreeFile(fp))
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not write file \"%s\": %m",
- histfilepath)));
-
- /*
- * Clean out any no-longer-needed history files. As a side effect, this will post a .ready file for the newly created history file, notifying the archiver that history file may be archived immediately. 清理备份历史信息
- */
- CleanupBackupHistory();
- }
-
- /*
- * 如果是归档模式,且设置了waitforarchive,则等待所需日志归档完成
- */
-
- if (waitforarchive &&
- ((!backup_started_in_recovery && XLogArchivingActive()) ||
- (backup_started_in_recovery && XLogArchivingAlways())))
- {
- //等待所需日志归档完成,略
- }
- /*
- * 如果不是归档模式,但又设置了waitforarchive,发送提醒给用户,需要自己保证备份期间的WAL文件存在
- */
- else if (waitforarchive)
- ereport(NOTICE,
- (errmsg("WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup")));
-
- /*
- * We're done. As a convenience, return the ending WAL location.
- * 函数执行结束,返回WAL结束位置
- */
- if (stoptli_p)
- *stoptli_p = stoptli;
- return stoppoint;
- }
前面代码中有一段
PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
确保如果PG_ENSURE_ERROR_CLEANUP中的代码运行失败,则将排他备份状态改回运行中。
- /*
- * Error cleanup callback for pg_stop_backup
- */
- static void
- pg_stop_backup_callback(int code, Datum arg)
- {
- bool exclusive = DatumGetBool(arg);
-
- /* Update backup status on failure */
- WALInsertLockAcquireExclusive();
- if (exclusive)
- {
- Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING);
- XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS;
- }
- WALInsertLockRelease();
- }
参考
《PostgreSQL技术内幕:事务处理深度探索》第4章