PostgreSQL-连续归档和时间点恢复 (PITR)
PostgreSQL 始终在群集数据目录的 pg_wal/
子目录中维护一个预写日志 (WAL)。该日志记录对数据库数据文件所做的每一项更改。此日志主要用于崩溃安全性:如果系统崩溃,可以通过“重放”自上次检查点以来所做的日志条目来将数据库恢复到一致性状态。但是,日志的存在使得可以使用第三种策略来备份数据库:我们可以将文件系统级备份与 WAL 文件备份结合起来。如果需要恢复,我们将恢复文件系统备份,然后从备份的 WAL 文件中重放,以使系统恢复到当前状态。与前两种方法相比,这种方法的管理更为复杂,但它有一些显著的优点
- 我们不需要一个完全一致的文件系统备份作为起点。备份中的任何内部不一致性都将通过日志重放来纠正(这与崩溃恢复期间发生的情况没有显着差异)。因此,我们不需要文件系统快照功能,只需 tar 或类似的归档工具即可。
- 由于我们可以组合无限长的 WAL 文件序列进行重放,因此只需继续归档 WAL 文件即可实现连续备份。这对于大型数据库特别有价值,因为频繁进行完全备份可能不方便。
- 没有必要将 WAL 条目重放到底。我们可以在任何时候停止重放,并获得当时数据库的一致快照。因此,此技术支持时间点恢复:自基本备份以来,可以随时将数据库恢复到其状态。
- 如果我们将一系列 WAL 文件连续馈送到已加载了相同基本备份文件的另一台计算机,那么我们就会拥有一个热备用系统:在任何时候,我们都可以启动第二台计算机,它将拥有数据库的近乎当前副本。
pg_dump 和 pg_dumpall 不会生成文件系统级别的备份,也不能用作连续归档解决方案的一部分。此类转储是逻辑的,不包含足够的信息以供 WAL 重放使用。
与普通文件系统备份技术一样,此方法只能支持整个数据库集群的恢复,而不是子集。此外,它需要大量的归档存储:基本备份可能很庞大,而繁忙的系统将生成许多兆字节的 WAL 流量,这些流量必须归档。尽管如此,在需要高可靠性的许多情况下,它仍然是首选的备份技术。
要使用连续归档(许多数据库供应商也称之为“在线备份”)成功恢复,您需要一个连续的已归档 WAL 文件序列,该序列至少可以追溯到备份的开始时间。因此,在进行第一次基本备份之前,您应设置并测试归档 WAL 文件的过程。因此,我们首先讨论归档 WAL 文件的机制。
1. 设置 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 归档更好,并且它们可以访问许多有用的服务器资源。有关归档模块的更多信息,请参阅 第 51 章。
当归档命令被信号终止(除了用作服务器关闭一部分的 SIGTERM)或 shell 出错,退出状态大于 125(例如未找到命令),或者如果归档函数发出 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 操作编辑的。您可能希望将配置文件保存在常规文件系统备份程序将备份的位置。有关如何重新定位配置文件,请参见 第 20.2 节。
存档命令或函数仅在完成的 WAL 段上调用。因此,如果您的服务器仅生成少量 WAL 流量(或在生成大量 WAL 流量时存在时段),则事务完成与其在存档存储中安全记录之间可能存在较长的延迟。要限制未存档数据可以保留多长时间,您可以设置 archive_timeout 以强制服务器至少如此频繁地切换到新的 WAL 段文件。请注意,由于强制切换而提前存档的存档文件仍然与完全满的文件长度相同。因此,不建议设置非常短的 archive_timeout
— 它会使您的存档存储膨胀。通常,一分钟左右的 archive_timeout
设置是合理的。
此外,如果您希望确保刚完成的事务尽快存档,可以使用 pg_switch_wal
手动强制执行段切换。与 WAL 管理相关的其他实用程序函数列于 表 9.91 中。
当 wal_level
为 minimal
时,某些 SQL 命令会进行优化以避免 WAL 日志记录,如 第 14.4.7 节 中所述。如果在执行其中一个语句期间启用了存档或流复制,则 WAL 将不包含足够的信息以供存档恢复。(崩溃恢复不受影响。)出于此原因,wal_level
只能在服务器启动时更改。但是,archive_command
和 archive_library
可以通过重新加载配置文件进行更改。如果您通过 shell 进行存档并且希望暂时停止存档,一种方法是将 archive_command
设置为空字符串 (''
)。这将导致 WAL 文件累积在 pg_wal/
中,直到重新建立可用的 archive_command
。
2. 创建基本备份
执行基本备份的最简单方法是使用 pg_basebackup 工具。它可以创建基本备份,形式为常规文件或 tar 存档。如果需要比 pg_basebackup 提供的更灵活的功能,您还可以使用低级 API 创建基本备份(请参阅 第 26.3.3 节)。
无需担心创建基本备份所需的时间。但是,如果您通常在禁用 full_page_writes
的情况下运行服务器,则您可能会注意到备份运行期间性能下降,因为在备份模式期间实际上强制启用了 full_page_writes
。
要利用备份,您需要保留文件系统备份期间和之后生成的所有 WAL 段文件。为了帮助您执行此操作,基本备份进程会创建一个备份历史文件,该文件会立即存储到 WAL 归档区域。此文件以文件系统备份所需的第一个 WAL 段文件命名。例如,如果起始 WAL 文件是0000000100001234000055CD
,则备份历史文件将被命名为类似于0000000100001234000055CD.007C9330.backup
的文件。(文件名第二部分表示 WAL 文件中的确切位置,通常可以忽略。)一旦您安全地归档了文件系统备份和备份期间使用的 WAL 段文件(如备份历史文件中指定的),所有名称数字较小的归档 WAL 段将不再需要恢复文件系统备份,可以删除。但是,您应该考虑保留多个备份集,以绝对确保您可以恢复数据。
备份历史文件只是一个小的文本文件。它包含您提供给pg_basebackup的标签字符串,以及备份的开始和结束时间和 WAL 段。如果您使用标签来识别关联的转储文件,则归档的历史文件足以告诉您要还原哪个转储文件。
由于您必须保留所有归档的 WAL 文件,直至上次基本备份,因此基本备份之间的间隔通常应根据您希望在归档的 WAL 文件上花费多少存储空间来选择。您还应考虑如果需要恢复,您准备花费多长时间进行恢复——系统必须重放所有这些 WAL 段,如果自上次基本备份以来已经过了很长时间,这可能会花费一段时间。
3. 使用低级 API 进行基本备份
使用低级 API 进行基本备份的过程比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))执行备份。在执行此操作时,无需也不希望停止数据库的正常操作。有关在此备份期间需要考虑的事项,请参阅 第 26.3.3.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,以便在停止备份记录写入 WAL 后立即返回pg_backup_stop
。默认情况下,pg_backup_stop
将等待,直到所有 WAL 都被存档,这可能需要一些时间。必须谨慎使用此选项:如果 WAL 存档未正确监控,则备份可能不包含所有 WAL 文件,因此不完整且无法还原。
3.1 备份数据目录
如果在复制过程中要复制的文件发生更改,某些文件系统备份工具会发出警告或错误。在对活动数据库进行基本备份时,这种情况是正常的,而不是错误。但是,您需要确保能够将此类投诉与实际错误区分开来。例如,某些版本的 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/
的内容(但不是目录本身)可以从备份中省略,因为它们将在启动后初始化。
以 pgsql_tmp
开头的任何文件或目录都可以从备份中省略。这些文件在启动后删除,目录将根据需要重新创建。
只要找到名为 pg_internal.init
的文件,就可以从备份中省略这些文件。这些文件包含在恢复时始终重建的关系缓存数据。
备份标签文件包括您提供给 pg_backup_start
的标签字符串,以及运行 pg_backup_start
的时间,以及起始 WAL 文件的名称。如果出现混淆,因此可以查看备份文件内部,并确定转储文件来自哪个备份会话。表空间映射文件包括符号链接名称(它们存在于目录 pg_tblspc/
中)和每个符号链接的完整路径。这些文件不仅仅供您了解;它们的存在及其内容对于系统恢复过程的正常操作至关重要。
服务器停止时也可以进行备份。在这种情况下,显然不能使用 pg_backup_start
或 pg_backup_stop
,因此只能靠自己来跟踪哪个备份是什么备份,以及关联的 WAL 文件回溯到什么程度。通常最好遵循上述连续归档过程。
4. 使用连续归档备份进行恢复
好吧,最糟糕的情况发生了,你需要从备份中恢复。以下是过程
- 如果服务器正在运行,请停止服务器。
- 如果有空间,请将整个群集数据目录和所有表空间复制到临时位置,以备以后需要。请注意,此预防措施要求系统上有足够的可用空间来容纳现有数据库的两个副本。如果没有足够的空间,你至少应该保存群集的
pg_wal
子目录的内容,因为它可能包含系统宕机前未归档的 WAL 文件。 - 删除群集数据目录和正在使用的任何表空间的根目录下的所有现有文件和子目录。
- 从文件系统备份中恢复数据库文件。确保以正确的权限(数据库系统用户,而不是
root
!)和正确的权限恢复它们。如果你正在使用表空间,则应该验证pg_tblspc/
中的符号链接是否已正确恢复。 - 删除
pg_wal/
中存在的任何文件;这些文件来自文件系统备份,因此可能是过时的而不是最新的。如果你根本没有归档pg_wal/
,那么请使用适当的权限重新创建它,小心确保重新将其建立为符号链接(如果你之前已按这种方式设置)。 - 如果你有在步骤 2 中保存的未归档 WAL 段文件,请将它们复制到
pg_wal/
中。(最好复制它们,而不是移动它们,这样如果出现问题并且你必须重新开始,你仍然拥有未修改的文件。) - 在
postgresql.conf
中设置恢复配置设置(请参阅 第 20.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 段尽可能接近)。因此,正常恢复将以 “file not found” 消息结束,确切的错误消息文本取决于你选择的 restore_command
。你可能还会在恢复开始时看到一个错误消息,用于类似 00000001.history
的文件。这也很正常,并且在简单的恢复情况下并不表示存在问题;请参阅 第 26.3.5 节 进行讨论。
如果您想恢复到某个之前的时间点(例如,就在初级 DBA 删除您的主要事务表之前),只需指定所需的 停止点。您可以通过日期/时间、命名的还原点或完成特定事务 ID 来指定停止点,即 “恢复目标”。在撰写本文时,只有日期/时间和命名的还原点选项非常可用,因为没有工具可以帮助您准确识别要使用哪个事务 ID。
停止点必须在基本备份的结束时间之后,即
pg_backup_stop
的结束时间。您不能使用基本备份恢复到该备份正在进行中的时间。(要恢复到这样的时间,您必须返回到先前的基本备份并从中向前滚动。)
如果恢复发现损坏的 WAL 数据,恢复将在该点停止,服务器将不会启动。在这种情况下,可以从头重新运行恢复过程,在损坏点之前指定一个 “恢复目标”,以便恢复可以正常完成。如果恢复由于外部原因(例如系统崩溃或 WAL 存档变得不可访问)而失败,那么可以简单地重新启动恢复,它将从失败的地方几乎重新启动。恢复重新启动的工作方式与正常操作中的检查点非常相似:服务器定期强制其所有状态转储到磁盘,然后更新 pg_control
文件以指示不需要再次扫描已处理的 WAL 数据。
5. 时间线
将数据库还原到以前时间点的能力会产生一些复杂性,类似于关于时间旅行和平行宇宙的科幻故事。例如,在数据库的原始历史中,假设您在星期二下午 5:15 删除了一个关键表,但直到星期三中午才意识到自己的错误。毫不犹豫,您拿出备份,还原到星期二下午 5:14 的时间点,并且正在运行。在 此 数据库宇宙的历史中,您从未删除过该表。但假设您后来意识到这不是一个好主意,并且希望回到原始历史中的星期三早上。如果您在数据库正在运行时覆盖了一些导致您现在希望返回的时间的 WAL 段文件,您将无法做到这一点。因此,为了避免这种情况,您需要区分在执行时间点恢复后生成的 WAL 记录序列和在原始数据库历史中生成的 WAL 记录序列。
为了解决这个问题,PostgreSQL 有一个时间线的概念。每当归档恢复完成时,都会创建一个新时间线来标识该恢复之后生成的一系列 WAL 记录。时间线 ID 号是 WAL 段文件名的一部分,因此新时间线不会覆盖由以前时间线生成 WAL 数据。例如,在 WAL 文件名 0000000100001234000055CD
中,开头的 00000001
是十六进制时间线 ID。(请注意,在其他上下文中(例如服务器日志消息),时间线 ID 通常以十进制打印。)
实际上可以归档许多不同的时间线。虽然这看起来像一个无用的功能,但它通常是救命稻草。考虑一下这种情况,你不太确定要恢复到哪个时间点,因此必须通过反复试验进行多次时间点恢复,直到找到从旧历史中分支的最佳位置。如果没有时间线,这个过程很快就会产生无法管理的混乱。有了时间线,你可以恢复到任何以前的状态,包括你之前放弃的时间线分支中的状态。
每次创建一个新时间线时,PostgreSQL 都会创建一个“时间线历史记录”文件,该文件显示它从哪个时间线分支出来以及何时分支出来。这些历史记录文件对于允许系统在从包含多个时间线的归档中恢复时选择正确 WAL 段文件是必需的。因此,它们像 WAL 段文件一样归档到 WAL 归档区域。历史记录文件只是小的文本文件,因此无限期地保留它们既便宜又合适(与大型段文件不同)。如果你愿意,可以向历史记录文件添加注释,以记录有关如何以及为何创建此特定时间线的自己的笔记。当由于实验而产生大量不同的时间线时,此类注释将特别有价值。
恢复的默认行为是恢复到归档中找到的最新时间线。如果你希望恢复到基本备份时当前的时间线或恢复到特定的子时间线(即,你想返回到恢复尝试后自身生成的一些状态),则需要在 recovery_target_timeline 中指定 current
或目标时间线 ID。你无法恢复到早于基本备份分支的时间线。
6. 提示和示例
这里给出了配置连续归档的一些提示。
6.1 独立热备份
可以使用 PostgreSQL 的备份工具来生成独立热备份。这些备份不能用于时间点恢复,但通常比 pg_dump 转储备份和恢复要快得多。(它们也比 pg_dump 转储大得多,因此在某些情况下速度优势可能会被抵消。)
与基础备份一样,生成独立热备份的最简单方法是使用 pg_basebackup 工具。如果在调用它时包含 -X
参数,则使用备份所需的所有预写式日志将自动包含在备份中,并且不需要执行任何特殊操作来恢复备份。
6.2 压缩归档日志
如果归档存储大小是个问题,则可以使用 gzip 压缩归档文件
archive_command = 'gzip < %p > /mnt/server/archivedir/%f.gz'
然后需要在恢复期间使用 gunzip
restore_command = 'gunzip < /mnt/server/archivedir/%f.gz > %p'
6.3 Archive_command
脚本
许多人选择使用脚本来定义其 archive_command
,以便其 postgresql.conf
条目看起来非常简单
archive_command = 'local_backup_script.sh "%p" "%f"'
在归档过程中需要使用多个命令时,建议使用单独的脚本文件。这允许在脚本中管理所有复杂性,该脚本可以用流行的脚本语言(如 bash 或 perl)编写。
可能在脚本中解决的要求示例包括
- 将数据复制到安全的异地数据存储
- 批量处理 WAL 文件,以便每隔三小时传输一次,而不是一次传输一个
- 与其他备份和恢复软件进行交互
- 与监控软件进行交互以报告错误
在使用
archive_command
脚本时,最好启用 logging_collector。然后,从脚本写入到 stderr 的任何消息都将出现在数据库服务器日志中,如果复杂配置失败,则可以轻松诊断。
7. 注意事项
在撰写本文时,连续归档技术存在一些限制。这些问题可能会在未来的版本中得到解决
- 如果在进行基础备份时执行
CREATE DATABASE
命令,然后在基础备份仍在进行时修改CREATE DATABASE
复制的模板数据库,则恢复可能会导致这些修改也传播到已创建的数据库中。这当然是不希望的。为了避免这种风险,最好在进行基础备份时不要修改任何模板数据库。 CREATE TABLESPACE
命令使用绝对路径的 WAL 日志记录,因此将作为具有相同绝对路径的表空间创建进行重播。如果 WAL 在不同的机器上重播,这可能是不希望的。即使 WAL 在同一台机器上重播,但进入新的数据目录,也可能很危险:重播仍将覆盖原始表空间的内容。为了避免此类潜在的陷阱,最佳做法是在创建或删除表空间后进行新的基本备份。
还应注意,默认 WAL 格式相当庞大,因为它包含许多磁盘页面快照。这些页面快照旨在支持崩溃恢复,因为我们可能需要修复部分写入的磁盘页面。根据系统硬件和软件,部分写入的风险可能小到可以忽略,在这种情况下,可以通过使用 full_page_writes 参数关闭页面快照来显著减少存档的 WAL 文件的总量。(在这样做之前,请阅读 第 30 章 中的注释和警告。)关闭页面快照不会阻止将 WAL 用于 PITR 操作。未来开发的一个领域是通过删除不必要的页面副本来压缩存档的 WAL 数据,即使 full_page_writes
已启用。与此同时,管理员可能希望通过尽可能增加检查点间隔参数来减少 WAL 中包含的页面快照数量。