在任何时候,PostgreSQL 都会在群集数据目录的 pg_wal/
子目录中维护一个预写式日志 (WAL)。该日志记录了对数据库数据文件所做的每一项更改。此日志主要用于崩溃安全目的:如果系统崩溃,则可以通过“重放”自上次检查点以来所做的日志条目,将数据库恢复到一致状态。但是,日志的存在使得可以使用第三种策略来备份数据库:我们可以将文件系统级别的备份与 WAL 文件的备份相结合。如果需要恢复,我们将恢复文件系统备份,然后从备份的 WAL 文件中重放,以使系统恢复到当前状态。这种方法比以前的任何一种方法都更复杂,但它有一些显著的好处。
我们不需要一个完全一致的文件系统备份作为起点。备份中的任何内部不一致都将通过日志重放来纠正(这与崩溃恢复期间发生的情况没有显著差异)。因此,我们不需要文件系统快照功能,只需 tar 或类似的归档工具即可。
由于我们可以结合无限长的 WAL 文件序列进行重放,因此只需继续归档 WAL 文件即可实现连续备份。这对于大型数据库尤其有价值,因为频繁进行完整备份可能不方便。
没有必要将 WAL 条目一直重放到最后。我们可以在任何时候停止重放,并获得数据库在该时间点的状态的一致快照。因此,这项技术支持时间点恢复:可以将数据库恢复到自您进行基础备份以来的任何时间的状态。
如果我们不断地将一系列 WAL 文件馈送到已加载相同基础备份文件的另一台机器,我们就有了一个热备系统:在任何时候我们都可以启动第二台机器,它将拥有数据库的近乎最新的副本。
pg_dump 和 pg_dumpall 不生成文件系统级别的备份,不能用作连续归档解决方案的一部分。此类转储是逻辑的,不包含足够的信息供 WAL 重放使用。
与普通的文件系统备份技术一样,此方法只能支持恢复整个数据库群集,而不是子集。此外,它需要大量的存档存储空间:基础备份可能很大,而繁忙的系统将生成许多兆字节的 WAL 流量,这些流量必须归档。尽管如此,在许多需要高可靠性的情况下,它仍然是首选的备份技术。
要使用连续归档(许多数据库供应商也称为“在线备份”)成功恢复,您需要一个连续的存档 WAL 文件序列,该序列至少可以追溯到备份的开始时间。因此,要开始使用,您应该在进行第一个基础备份之前设置并测试您的 WAL 文件归档过程。因此,我们首先讨论归档 WAL 文件的机制。
从抽象意义上讲,运行的 PostgreSQL 系统会生成无限长的 WAL 记录序列。系统将此序列物理划分为 WAL 段文件,通常每个文件为 16MB(尽管段大小可以在 initdb 期间更改)。段文件被赋予数字名称,这些名称反映了它们在抽象 WAL 序列中的位置。当不使用 WAL 归档时,系统通常只创建几个段文件,然后通过将不再需要的段文件重命名为更高的段编号来“回收”它们。假设内容位于上次检查点之前的段文件不再受关注,可以回收。
当归档 WAL 数据时,我们需要捕获每个段文件填满后的内容,并在该段文件被回收以供重用之前将该数据保存到某个地方。根据应用程序和可用的硬件,可能有许多不同的方法来“将数据保存到某个地方”:我们可以将段文件复制到另一台机器上的 NFS 挂载目录中,将它们写入磁带驱动器(确保您有一种方法可以识别每个文件的原始名称),或者将它们批量组合在一起并刻录到 CD 上,或者其他完全不同的方法。为了向数据库管理员提供灵活性,PostgreSQL 尽量不对归档方式做任何假设。相反,PostgreSQL 允许管理员指定一个 shell 命令或一个存档库来执行,以将完成的段文件复制到它需要去的地方。这可以像使用 cp
的 shell 命令一样简单,也可以调用一个复杂的 C 函数 — 这完全取决于您。
要启用 WAL 归档,请将 wal_level 配置参数设置为 replica
或更高,将 archive_mode 设置为 on
,在 archive_command 配置参数中指定要使用的 shell 命令,或者在 archive_library 配置参数中指定要使用的库。实际上,这些设置将始终放置在 postgresql.conf
文件中。
在 archive_command
中,%p
被替换为要归档的文件的路径名,而 %f
仅被替换为文件名。(路径名是相对于当前工作目录的,即群集的数据目录。)如果需要在命令中嵌入实际的 %
字符,请使用 %%
。最简单的有用命令如下所示
archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' # Unix archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"' # Windows
这将把可归档的 WAL 段复制到目录 /mnt/server/archivedir
。(这是一个示例,而不是建议,并且可能并非在所有平台上都有效。)在替换 %p
和 %f
参数之后,执行的实际命令可能如下所示
test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_wal/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065
将为每个要归档的新文件生成类似的命令。
归档命令将以运行 PostgreSQL 服务器的同一用户的身份执行。由于要归档的一系列 WAL 文件实际上包含您数据库中的所有内容,因此您需要确保归档的数据受到保护,不会被窥视;例如,归档到没有组或世界读取访问权限的目录中。
重要的是,归档命令仅在其成功时才返回零退出状态。在获得零结果后,PostgreSQL 将假定该文件已成功归档,并将删除或回收它。但是,非零状态会告诉 PostgreSQL 该文件未归档;它将定期重试,直到成功。
另一种归档方式是将自定义归档模块用作 archive_library
。由于此类模块是用 C
编写的,因此创建自己的模块可能需要比编写 shell 命令付出更多的努力。但是,归档模块可能比通过 shell 进行归档的性能更高,并且它们将可以访问许多有用的服务器资源。有关归档模块的更多信息,请参阅第 49 章。
当归档命令因信号(服务器关闭时使用的 SIGTERM 除外)或退出状态大于 125(例如找不到命令)的 shell 错误而终止时,或者如果归档函数发出 ERROR
或 FATAL
,则归档程序进程会中止并由 postmaster 重新启动。在这种情况下,失败不会在 pg_stat_archiver 中报告。
归档命令和库的设计通常应拒绝覆盖任何预先存在的归档文件。这是一个重要的安全特性,可以在发生管理员错误(例如将两个不同服务器的输出发送到同一个归档目录)时,保护归档的完整性。建议测试您提出的归档库,以确保它不会覆盖现有文件。
在极少数情况下,PostgreSQL 可能会尝试重新归档先前已归档的 WAL 文件。例如,如果系统在服务器持久记录归档成功之前崩溃,则服务器将在重启后(如果仍启用归档)尝试再次归档该文件。当归档命令或库遇到预先存在的文件时,如果 WAL 文件与预先存在的归档文件的内容相同,并且预先存在的归档文件已完全持久化到存储,则应分别返回零状态或 true
。如果预先存在的文件包含与正在归档的 WAL 文件不同的内容,则归档命令或库必须分别返回非零状态或 false
。
上面针对 Unix 的示例命令通过包含单独的 test
步骤来避免覆盖预先存在的归档。在某些 Unix 平台上,cp
具有诸如 -i
之类的开关,可用于以较少冗余的方式执行相同的操作,但是您不应依赖这些开关,而应验证是否返回了正确的退出状态。(特别是,当使用 -i
并且目标文件已经存在时,GNU cp
将返回状态零,这不是期望的行为。)
在设计归档设置时,请考虑如果归档命令或库由于某些方面需要操作员干预或归档空间不足而重复失败会发生什么情况。例如,如果写入磁带而没有自动换带机,则可能会发生这种情况;当磁带填满时,在更换磁带之前,将无法归档任何内容。您应确保任何错误情况或对人工操作员的请求都得到适当报告,以便可以合理地快速解决问题。pg_wal/
目录将继续填充 WAL 段文件,直到问题解决。(如果包含 pg_wal/
的文件系统已满,则 PostgreSQL 将执行 PANIC 关闭。不会丢失任何已提交的事务,但数据库将保持脱机状态,直到您释放一些空间为止。)
只要归档命令或库能够跟上服务器生成 WAL 数据的平均速率,它的速度就不重要。即使归档过程稍微落后,正常操作也会继续。如果归档严重滞后,这将增加在发生灾难时可能丢失的数据量。这也意味着 pg_wal/
目录将包含大量尚未归档的段文件,这些文件最终可能会超出可用磁盘空间。建议您监视归档过程,以确保它按预期工作。
在编写归档命令或库时,您应假设要归档的文件名最长可达 64 个字符,并且可以包含 ASCII 字母、数字和点号的任意组合。无需保留原始的相对路径(%p
),但必须保留文件名(%f
)。
请注意,尽管 WAL 归档允许您恢复对 PostgreSQL 数据库中的数据所做的任何修改,但它不会恢复对配置文件(即 postgresql.conf
、pg_hba.conf
和 pg_ident.conf
)所做的更改,因为这些文件是手动编辑的,而不是通过 SQL 操作编辑的。您可能希望将配置文件保存在可以通过常规文件系统备份程序备份的位置。有关如何重新定位配置文件,请参阅 第 19.2 节。
仅在完成 WAL 段时才调用归档命令或函数。因此,如果您的服务器仅生成少量 WAL 流量(或者有产生少量 WAL 流量的空闲时段),则事务完成与其安全记录在归档存储中之间可能会有很长的延迟。要限制未归档数据的旧程度,您可以设置 archive_timeout,以强制服务器至少以该频率切换到新的 WAL 段文件。请注意,由于强制切换而提前归档的归档文件仍然与完全满的文件长度相同。因此,设置非常短的 archive_timeout
是不明智的,它会使您的归档存储膨胀。archive_timeout
设置为一分钟左右通常是合理的。
此外,如果您想确保刚刚完成的事务尽快归档,则可以使用 pg_switch_wal
手动强制进行段切换。与 WAL 管理相关的其他实用程序函数在 表 9.95 中列出。
当 wal_level
为 minimal
时,某些 SQL 命令会被优化以避免 WAL 日志记录,如 第 14.4.7 节所述。如果在执行这些语句之一期间启用了归档或流复制,则 WAL 将不包含足够的用于归档恢复的信息。(崩溃恢复不受影响。)因此,只能在服务器启动时更改 wal_level
。但是,可以使用配置文件重新加载来更改 archive_command
和 archive_library
。如果您通过 shell 进行归档,并且希望暂时停止归档,一种方法是将 archive_command
设置为空字符串(''
)。这将导致 WAL 文件在 pg_wal/
中累积,直到重新建立有效的 archive_command
。
执行基本备份的最简单方法是使用 pg_basebackup 工具。它可以将基本备份创建为常规文件或 tar 归档。如果需要比 pg_basebackup 可以提供的更大的灵活性,您还可以使用低级 API 进行基本备份(请参阅 第 25.3.4 节)。
无需担心制作基本备份所需的时间。但是,如果您通常在禁用 full_page_writes
的情况下运行服务器,您可能会注意到在备份运行时性能下降,因为在备份模式下会有效地强制启用 full_page_writes
。
要使用备份,您需要保留在文件系统备份期间和之后生成的所有 WAL 段文件。为了帮助您执行此操作,基本备份过程会创建一个备份历史文件,该文件会立即存储到 WAL 归档区域中。该文件以文件系统备份所需的第一个 WAL 段文件命名。例如,如果起始 WAL 文件为 0000000100001234000055CD
,则备份历史文件将类似于 0000000100001234000055CD.007C9330.backup
。(文件名的第二部分表示 WAL 文件中的确切位置,通常可以忽略。)在您安全地归档文件系统备份以及备份期间使用的 WAL 段文件(如备份历史文件中指定的)后,所有名称在数字上较小的已归档 WAL 段不再需要用于恢复文件系统备份,可以删除。但是,您应考虑保留多个备份集,以绝对确保可以恢复您的数据。
备份历史文件只是一个小文本文件。它包含您给 pg_basebackup 的标签字符串,以及备份的开始和结束时间和 WAL 段。如果您使用该标签来标识关联的转储文件,则已归档的历史文件足以告诉您要还原哪个转储文件。
由于您必须保留所有已归档的 WAL 文件,直到上次基本备份为止,因此基本备份之间的间隔通常应根据您希望在已归档的 WAL 文件上花费多少存储空间来选择。您还应考虑如果需要恢复,您愿意花费多长时间进行恢复 — 系统将必须重播所有这些 WAL 段,如果距离上次基本备份已经很长时间,则可能需要一段时间。
您可以使用 pg_basebackup 通过指定 --incremental
选项来执行增量备份。您必须提供作为 --incremental
的参数,该参数是来自同一服务器的先前备份的备份清单。在生成的备份中,非关系文件将完整包含,但是某些关系文件可能会被较小的增量文件替换,这些增量文件仅包含自先前备份以来已更改的块以及足够重建该文件当前版本的元数据。
为了确定哪些数据块需要备份,服务器会使用 WAL 摘要,这些摘要存储在数据目录的 pg_wal/summaries
目录下。如果所需的摘要文件不存在,则尝试进行增量备份将失败。此目录中的摘要必须涵盖从先前备份的起始 LSN 到当前备份的起始 LSN 的所有 LSN。由于服务器在确定当前备份的起始 LSN 后会立即查找 WAL 摘要,因此必要的摘要文件可能不会立即出现在磁盘上,但服务器会等待任何缺失的文件出现。如果 WAL 摘要进程滞后,这也很有帮助。但是,如果必要的文件已被删除,或者 WAL 摘要器没有及时赶上进度,则增量备份将失败。
在还原增量备份时,不仅需要增量备份本身,还需要提供增量备份中省略的数据块的所有早期备份。有关此要求的更多信息,请参阅 pg_combinebackup。请注意,当集群的校验和状态发生更改时,对 pg_combinebackup
的使用存在限制;请参阅 pg_combinebackup 限制。
请注意,使用完整备份的所有要求也适用于增量备份。例如,您仍然需要文件系统备份期间和之后生成的所有 WAL 段文件,以及任何相关的 WAL 历史文件。并且您仍然需要创建一个 recovery.signal
(或 standby.signal
)并执行恢复,如 第 25.3.5 节中所述。在还原时需要可用早期备份并使用 pg_combinebackup
是在所有其他要求之上附加的要求。请记住,PostgreSQL 没有内置机制来确定哪些备份仍然需要作为还原后续增量备份的基础。您必须自行跟踪完整备份和增量备份之间的关系,并确保在还原后续增量备份时可能需要用到早期备份的情况下,不要删除它们。
增量备份通常只适用于数据更改不频繁或缓慢的相对较大的数据库。对于小型数据库,忽略增量备份的存在并只进行完整备份更简单,因为完整备份更易于管理。对于大量修改的大型数据库,增量备份不会比完整备份小很多。
只有当回放从比其所依赖的前一个备份更晚的检查点开始时,才有可能进行增量备份。如果在主服务器上进行增量备份,则始终满足此条件,因为每次备份都会触发一个新的检查点。在备用服务器上,回放从最近的重启点开始。因此,如果自上次备份以来活动很少,则备用服务器的增量备份可能会失败,因为可能没有创建新的重启点。
您可以使用底层 API 进行基本备份,而不是使用 pg_basebackup 进行完整或增量基本备份。此过程比 pg_basebackup 方法多一些步骤,但相对简单。按顺序执行这些步骤非常重要,并且必须在继续下一步之前验证步骤是否成功。
可以同时运行多个备份(包括使用此备份 API 启动的备份和使用 pg_basebackup 启动的备份)。
确保 WAL 归档已启用且正常工作。
以有权运行 pg_backup_start
的用户(超级用户或已被授予该函数 EXECUTE
权限的用户)身份连接到服务器(哪个数据库无关紧要),并发出以下命令
SELECT pg_backup_start(label => 'label', fast => false);
其中 label
是您要用来唯一标识此备份操作的任何字符串。调用 pg_backup_start
的连接必须保持到备份结束,否则备份将自动中止。
联机备份始终在检查点的开头启动。默认情况下,pg_backup_start
将等待下一个定期计划的检查点完成,这可能需要很长时间(请参阅配置参数 checkpoint_timeout 和 checkpoint_completion_target)。这通常是首选,因为它最大限度地减少了对运行系统的影响。如果您想尽快开始备份,请将 true
作为第二个参数传递给 pg_backup_start
,它将请求立即进行检查点,该检查点将使用尽可能多的 I/O 尽快完成。
使用任何方便的文件系统备份工具(如 tar 或 cpio(而不是 pg_dump 或 pg_dumpall))执行备份。在执行此操作时,没有必要也不希望停止数据库的正常运行。有关此备份期间需要考虑的事项,请参阅 第 25.3.4.1 节。
在之前的同一连接中,发出以下命令
SELECT * FROM pg_backup_stop(wait_for_archive => true);
这会终止备份模式。在主服务器上,它还会自动切换到下一个 WAL 段。在备用服务器上,无法自动切换 WAL 段,因此您可能希望在主服务器上运行 pg_switch_wal
以执行手动切换。切换的原因是安排在备份间隔期间写入的最后一个 WAL 段文件准备好进行归档。
pg_backup_stop
将返回包含三个值的一行。这些字段中的第二个字段应写入备份根目录中名为 backup_label
的文件。除非该字段为空,否则第三个字段应写入名为 tablespace_map
的文件。这些文件对于备份工作至关重要,必须按字节写入,不得修改,这可能需要以二进制模式打开文件。
一旦备份期间处于活动状态的 WAL 段文件被归档,就完成了。由 pg_backup_stop
的第一个返回值标识的文件是形成完整备份文件集所需的最后一个段。在主服务器上,如果启用了 archive_mode
并且 wait_for_archive
参数为 true
,则 pg_backup_stop
不会返回,直到最后一个段被归档。在备用服务器上,archive_mode
必须为 always
,pg_backup_stop
才会等待。这些文件的归档会自动进行,因为您已经配置了 archive_command
或 archive_library
。在大多数情况下,这种情况会很快发生,但建议您监控归档系统,以确保没有延迟。如果由于归档命令或库的失败而导致归档过程滞后,它将不断重试,直到归档成功且备份完成。如果您希望对 pg_backup_stop
的执行设置时间限制,请设置适当的 statement_timeout
值,但请注意,如果 pg_backup_stop
因该值而终止,则您的备份可能无效。
如果备份进程监控并确保成功归档备份所需的所有 WAL 段文件,则可以将 wait_for_archive
参数(默认为 true)设置为 false,以便 pg_backup_stop
在将停止备份记录写入 WAL 后立即返回。默认情况下,pg_backup_stop
将等待直到所有 WAL 都被归档,这可能需要一些时间。必须谨慎使用此选项:如果未正确监视 WAL 归档,则备份可能不包含所有 WAL 文件,因此将不完整且无法还原。
如果文件在复制过程中发生更改,某些文件系统备份工具会发出警告或错误。在对活动数据库进行基本备份时,这种情况是正常的,而不是错误。但是,您需要确保能够将此类错误与实际错误区分开来。例如,某些版本的 rsync 为“消失的源文件”返回单独的退出代码,您可以编写一个驱动程序脚本来接受此退出代码作为非错误情况。此外,如果文件在 tar 复制时被截断,某些版本的 GNU tar 会返回一个与致命错误无法区分的错误代码。幸运的是,如果文件在备份期间发生更改,GNU tar 1.16 及更高版本将退出并返回 1,而对于其他错误则返回 2。使用 GNU tar 1.23 及更高版本,您可以使用警告选项 --warning=no-file-changed --warning=no-file-removed
来隐藏相关的警告消息。
请确保您的备份包含数据库集群目录(例如,/usr/local/pgsql/data
)下的所有文件。如果您正在使用不在此目录下的表空间,请注意也将它们包括在内(并确保您的备份将符号链接作为链接进行归档,否则还原会损坏您的表空间)。
但是,您应该从备份中省略集群的 pg_wal/
子目录中的文件。这种细微的调整是值得的,因为它降低了还原时出错的风险。如果 pg_wal/
是指向集群目录外部某个位置的符号链接,则很容易安排这种情况,这在任何情况下都是出于性能原因的常见设置。您可能还想排除 postmaster.pid
和 postmaster.opts
,它们记录有关正在运行的 postmaster 的信息,而不是有关最终将使用此备份的 postmaster 的信息。(这些文件会使 pg_ctl 感到困惑。)
通常,最好将集群的 pg_replslot/
目录中的文件也从备份中省略,这样主节点上存在的复制槽就不会成为备份的一部分。否则,后续使用备份创建备用节点可能会导致备用节点无限期保留 WAL 文件,并且如果启用了热备反馈,则可能会导致主节点膨胀,因为使用这些复制槽的客户端仍然会连接并更新主节点上的槽,而不是备用节点上的槽。即使备份仅用于创建新的主节点,复制复制槽也不是特别有用,因为到新主节点上线时,这些槽的内容很可能已经严重过时。
目录 pg_dynshmem/
、 pg_notify/
、pg_serial/
、pg_snapshots/
、pg_stat_tmp/
和 pg_subtrans/
(但不是目录本身)的内容可以从备份中省略,因为它们将在 postmaster 启动时初始化。
任何以 pgsql_tmp
开头的文件或目录都可以从备份中省略。这些文件在 postmaster 启动时会被删除,并且目录会根据需要重新创建。
每当找到名为 pg_internal.init
的文件时,都可以从备份中省略这些文件。这些文件包含关系缓存数据,这些数据在恢复时始终会重建。
备份标签文件包含您提供给 pg_backup_start
的标签字符串,以及 pg_backup_start
运行的时间和起始 WAL 文件的名称。因此,在出现混淆的情况下,可以查看备份文件并确定转储文件来自哪个备份会话。表空间映射文件包含 pg_tblspc/
目录中存在的符号链接名称以及每个符号链接的完整路径。这些文件不仅仅是为了您的信息;它们的存在和内容对于系统恢复过程的正确运行至关重要。
也可以在服务器停止时进行备份。在这种情况下,您显然不能使用 pg_backup_start
或 pg_backup_stop
,因此您只能自己跟踪哪个备份是哪个,以及相关的 WAL 文件回溯到多远。通常,最好遵循上述连续归档过程。
好吧,最糟糕的情况发生了,您需要从备份中恢复。以下是步骤
如果服务器正在运行,请停止服务器。
如果有足够的空间,请将整个集群数据目录和任何表空间复制到临时位置,以备将来需要。请注意,此预防措施将要求您的系统上有足够的可用空间来保存现有数据库的两个副本。如果您没有足够的空间,您至少应该保存集群的 pg_wal
子目录的内容,因为它可能包含在系统关闭之前未归档的 WAL 文件。
删除集群数据目录和您正在使用的任何表空间的根目录下的所有现有文件和子目录。
如果您要恢复完整备份,则可以将数据库文件直接恢复到目标目录中。请确保使用正确的权限(数据库系统用户,而不是 root
!)和正确的权限恢复它们。如果您正在使用表空间,则应验证 pg_tblspc/
中的符号链接是否已正确恢复。
如果您要恢复增量备份,则需要将增量备份和它直接或间接依赖的所有早期备份恢复到执行恢复的计算机上。这些备份需要放置在单独的目录中,而不是您希望正在运行的服务器最终所在的目标目录中。完成此操作后,请使用 pg_combinebackup 从完整备份和所有后续增量备份中提取数据,并将合成完整备份写入目标目录。如上所述,验证权限和表空间链接是否正确。
删除 pg_wal/
中存在的任何文件;这些文件来自文件系统备份,因此可能已过时而不是最新的。如果您根本没有归档 pg_wal/
,则使用正确的权限重新创建它,请务必确保将其重新建立为符号链接(如果您之前已这样设置)。
如果您在步骤 2 中保存了未归档的 WAL 段文件,请将它们复制到 pg_wal/
中。(最好复制它们,而不是移动它们,这样如果出现问题并且您必须重新开始,您仍然拥有未修改的文件。)
在 postgresql.conf
中设置恢复配置设置(请参阅 第 19.5.5 节),并在集群数据目录中创建一个文件 recovery.signal
。您可能还希望临时修改 pg_hba.conf
,以防止普通用户连接,直到您确定恢复成功为止。
启动服务器。服务器将进入恢复模式,并开始读取它需要的已归档的 WAL 文件。如果由于外部错误而终止了恢复,则可以简单地重新启动服务器,它将继续恢复。恢复过程完成后,服务器将删除 recovery.signal
(以防止以后意外地重新进入恢复模式),然后开始正常的数据库操作。
检查数据库的内容,以确保您已恢复到所需的状态。如果不是,请返回步骤 1。如果一切正常,请将 pg_hba.conf
恢复为正常状态,以允许用户连接。
所有这些的关键部分是设置一个恢复配置,该配置描述了您希望如何恢复以及恢复应该运行多远。您绝对必须指定的一件事是 restore_command
,它告诉 PostgreSQL 如何检索已归档的 WAL 文件段。与 archive_command
一样,这是一个 shell 命令字符串。它可以包含 %f
,它被替换为所需的 WAL 文件的名称,以及 %p
,它被替换为用于复制 WAL 文件的路径名。(路径名是相对于当前工作目录的,即集群的数据目录。)如果您需要在命令中嵌入实际的 %
字符,请写 %%
。最简单的有用命令类似于
restore_command = 'cp /mnt/server/archivedir/%f %p'
这将从目录 /mnt/server/archivedir
中复制先前归档的 WAL 段。当然,您可以使用更复杂的东西,甚至可以使用一个 shell 脚本,该脚本请求操作员装载适当的磁带。
重要的是,该命令在失败时返回非零退出状态。该命令将被调用来请求存档中不存在的文件;当被这样要求时,它必须返回非零值。这不是错误情况。一个例外是,如果命令由于信号(SIGTERM 除外,它用作数据库服务器关闭的一部分)或 shell 错误(例如未找到命令)而终止,则恢复将中止,并且服务器将不会启动。
并非所有请求的文件都是 WAL 段文件;您还应该期望请求带有 .history
后缀的文件。还要注意,%p
路径的基本名称将与 %f
不同;不要期望它们是可互换的。
在存档中找不到的 WAL 段将在 pg_wal/
中查找;这允许使用最近未归档的段。但是,从存档中可用的段将优先于 pg_wal/
中的文件使用。
通常,恢复将通过所有可用的 WAL 段进行,从而将数据库恢复到当前时间点(或在给定可用 WAL 段的情况下尽可能接近的时间点)。因此,正常的恢复将以 “未找到文件” 消息结束,错误消息的确切文本取决于您选择的 restore_command
。您还可能会在恢复开始时看到一个错误消息,其中包含类似于 00000001.history
的文件名。这也是正常的,并且在简单的恢复情况下并不表示存在问题;有关讨论,请参阅 第 25.3.6 节。
如果您想恢复到某个先前的时间点(例如,在初级 DBA 删除您的主要事务表之前),只需指定所需的 停止点即可。您可以通过日期/时间、命名的还原点或完成特定事务 ID 来指定停止点,即所谓的 “恢复目标”。截至本文撰写之时,只有日期/时间和命名还原点选项非常可用,因为没有工具可以帮助您准确地识别要使用的事务 ID。
停止点必须在基本备份的结束时间之后,即 pg_backup_stop
的结束时间。您不能使用基本备份来恢复到该备份正在进行的时间。(要恢复到这样的时间,您必须返回到以前的基本备份并从那里开始前滚。)
如果恢复发现损坏的 WAL 数据,则恢复将在该点停止,并且服务器将不会启动。在这种情况下,可以从头开始重新运行恢复过程,并在损坏点之前指定 “恢复目标”,以便恢复可以正常完成。如果恢复由于外部原因(例如系统崩溃或 WAL 存档变得不可访问)而失败,则可以简单地重新启动恢复,它将几乎从失败的地方重新开始。恢复重新启动的工作方式与正常操作中的检查点非常相似:服务器会定期将其所有状态强制写入磁盘,然后更新 pg_control
文件以指示已处理的 WAL 数据不需要再次扫描。
将数据库恢复到之前的某个时间点的能力带来了一些复杂性,这类似于关于时间旅行和平行宇宙的科幻故事。例如,在数据库的原始历史记录中,假设您在星期二晚上 5:15 删除了一个关键表,但直到星期三中午才意识到您的错误。您不慌不忙地拿出备份,恢复到星期二晚上 5:14 的时间点,然后启动并运行。在数据库宇宙的这个历史记录中,您从未删除该表。但是,假设您稍后意识到这不是一个好主意,并希望回到原始历史记录中的某个星期三早上。如果您的数据库在运行期间覆盖了一些导致您现在希望返回的时间点的 WAL 段文件,您将无法返回。因此,为了避免这种情况,您需要区分在执行时间点恢复后生成的 WAL 记录序列与原始数据库历史记录中生成的记录。
为了解决这个问题,PostgreSQL 引入了时间线的概念。每当存档恢复完成时,都会创建一个新的时间线来标识在该恢复之后生成的 WAL 记录序列。时间线 ID 号是 WAL 段文件名称的一部分,因此新的时间线不会覆盖先前时间线生成的 WAL 数据。例如,在 WAL 文件名 0000000100001234000055CD
中,前导 00000001
是十六进制的时间线 ID。(请注意,在其他上下文中,例如服务器日志消息中,时间线 ID 通常以十进制打印。)
实际上,可以存档许多不同的时间线。虽然这看起来似乎是一个无用的功能,但它通常是救命稻草。考虑一下您不确定要恢复到哪个时间点的情况,因此必须通过反复试验执行多次时间点恢复,直到找到从旧历史记录中分支出来的最佳位置。如果没有时间线,此过程很快会产生无法管理的混乱。有了时间线,您可以恢复到任何先前的状态,包括您之前放弃的时间线分支中的状态。
每次创建新时间线时,PostgreSQL 都会创建一个“时间线历史”文件,该文件显示它从哪个时间线分支出来以及何时分支。这些历史文件是必需的,以便系统在从包含多个时间线的存档中恢复时选择正确的 WAL 段文件。因此,它们像 WAL 段文件一样被存档到 WAL 存档区域。历史文件只是小的文本文件,因此可以廉价且适当地无限期地保留它们(与大型段文件不同)。如果您愿意,可以在历史文件中添加注释,以记录您自己关于如何以及为何创建此特定时间线的注释。当您由于实验而拥有大量不同的时间线时,这些注释将尤其有价值。
恢复的默认行为是恢复到存档中找到的最新时间线。如果您希望恢复到拍摄基本备份时的时间线,或者恢复到特定的子时间线(也就是说,您想要返回到在恢复尝试后生成的某个状态),则需要在 recovery_target_timeline 中指定 current
或目标时间线 ID。您无法恢复到早于基本备份分支的时间线。
此处提供了一些用于配置连续存档的提示。
可以使用 PostgreSQL 的备份工具来生成独立的 热备份。这些备份不能用于时间点恢复,但备份和恢复速度通常比 pg_dump 转储快得多。(它们也比 pg_dump 转储大得多,因此在某些情况下,速度优势可能会被抵消。)
与基本备份一样,生成独立热备份的最简单方法是使用 pg_basebackup 工具。如果在调用它时包含 -X
参数,则使用备份所需的所有预写式日志将自动包含在备份中,并且无需执行任何特殊操作即可还原备份。
如果存档存储大小是一个问题,您可以使用 gzip 来压缩存档文件
archive_command = 'gzip < %p > /mnt/server/archivedir/%f.gz'
然后,您需要在恢复期间使用 gunzip
restore_command = 'gunzip < /mnt/server/archivedir/%f.gz > %p'
archive_command
脚本 #许多人选择使用脚本来定义他们的 archive_command
,以便他们的 postgresql.conf
条目看起来非常简单
archive_command = 'local_backup_script.sh "%p" "%f"'
当您想在存档过程中使用多个命令时,建议使用单独的脚本文件。这允许在脚本中管理所有复杂性,该脚本可以使用流行的脚本语言(如 bash 或 perl)编写。
可以在脚本中解决的需求示例包括
将数据复制到安全的异地数据存储
批量处理 WAL 文件,以便每三小时传输一次,而不是一次一个
与其它备份和恢复软件对接
与监控软件对接以报告错误
使用 archive_command
脚本时,最好启用 logging_collector。然后,从脚本写入 stderr 的任何消息都将出现在数据库服务器日志中,从而允许在复杂配置失败时轻松诊断它们。
在撰写本文时,连续存档技术存在一些限制。这些问题可能会在未来的版本中得到解决。
如果在拍摄基本备份时执行 CREATE DATABASE
命令,然后在基本备份仍在进行时修改 CREATE DATABASE
复制的模板数据库,则恢复可能会导致这些修改也传播到创建的数据库中。这当然是不希望看到的。为避免此风险,最好在执行基本备份时不要修改任何模板数据库。
CREATE TABLESPACE
命令使用文字绝对路径进行 WAL 日志记录,因此将作为具有相同绝对路径的表空间创建来重放。如果在不同的机器上重放 WAL,这可能是不希望的。即使 WAL 在同一机器上重放,但进入新的数据目录,也可能存在危险:重放仍将覆盖原始表空间的内容。为避免此类潜在的陷阱,最佳实践是在创建或删除表空间后执行新的基本备份。
还应注意,默认的WAL格式相当笨重,因为它包含许多磁盘页面快照。这些页面快照旨在支持崩溃恢复,因为我们可能需要修复部分写入的磁盘页面。根据您的系统硬件和软件,部分写入的风险可能足够小,可以忽略不计,在这种情况下,您可以通过使用 full_page_writes 参数关闭页面快照来显着减少存档 WAL 文件的总量。(在执行此操作之前,请阅读 第 28 章 中的说明和警告。)关闭页面快照不会阻止将 WAL 用于 PITR 操作。未来的一个发展方向是在 full_page_writes
打开时,通过删除不必要的页面副本来压缩存档的 WAL 数据。同时,管理员可能希望通过尽可能增加检查点间隔参数来减少 WAL 中包含的页面快照数量。
如果您在文档中发现任何不正确、与特定功能不符或需要进一步澄清的内容,请使用此表格报告文档问题。