PostgreSQL的增量备份和时间点还原(PITR)
📅 2024-12-12 | 🖱️
备份PostgreSQL有三种不同的方法:
- SQL Dump
- 文件系统级备份 - (限制:数据库服务器必须关闭才能获得可用的备份)
- 持续归档和时间点还原(PITR)
在任何时候,PostgreSQL都会在集群数据目录的pg_wal/
子目录中维护一个写前日志(WAL)。该日志记录了对数据库数据文件所做的每个更改。这个日志主要用于崩溃安全:如果系统崩溃,可以通过“重放”自上次检查点以来的日志条目,将数据库恢复到一致性状态。WAL日志的存在使得使用第三种备份策略成为可能:我们可以将文件系统级备份与WAL文件的备份相结合。如果需要恢复,只需恢复文件系统备份,再重放备份的WAL文件,将系统恢复到当前状态。这种方法比前两种方法更复杂,但它有一些显著的优势:
- 不需要一个完全一致的文件系统备份作为起点。备份中的任何内部不一致将通过日志重放得到修正(这与崩溃恢复期间发生的情况没有显著不同)。因此,不需要文件系统快照功能,只需要tar或类似的归档工具。
- 由于可以结合无限长的WAL文件序列进行重放,因此可以通过继续归档WAL文件来实现连续备份。这对于大型数据库特别有价值,因为频繁进行完整备份在这些数据库中可能不太方便。
- 无需将WAL条目重放至结束。我们可以在任何时候停止重放,并获得数据库在当时的一致快照。因此,这种技术支持按时间点恢复:可以将数据库恢复到自基准备份以来的任何时间点。
- 如果持续将一系列WAL文件传送到另一台已经加载了相同基准备份文件的机器上,就可以构建一个热备用系统:在任何时候,都可以启动第二台机器,它将拥有数据库的近乎当前副本。
注意
注意
pg_dump
和pg_dumpall
并不会生成文件系统级别的备份,因此不能作为连续归档解决方案的一部分。这些是逻辑备份,包含的信息不足以被WAL重放使用。
与纯文件系统备份技术一样,这种方法只能支持恢复整个数据库集群,而不是其子集。此外,它需要大量的归档存储:基准备份可能会非常大,并且一个繁忙的系统会生成大量的WAL,这些WAL需要归档。尽管如此,在许多需要高可靠性的情况下,这仍然是首选的备份技术。
要通过连续归档(许多数据库供应商也称之为“在线备份”)成功恢复,需要一系列连续归档的WAL文件,归档时间需至少追溯到备份开始时。因此,在开始之前,应该设置并测试归档WAL文件的过程,然后再进行第一次基准备份。因此,首先讨论归档WAL文件的机制。
设置WAL归档 #
WAL段文件概述 #
正在运行的PostgreSQL会生成一个无限长的WAL记录序列。系统将该序列物理地划分为WAL段文件(WAL segment files),通常每个文件大小为16MB(在initdb过程时可以更改段大小)。这些段文件被赋予反映它们在WAL序列中位置的数字名称。在不使用WAL归档时,系统通常只创建几个段文件,然后通过将不再需要的段文件重命名为更高的段号来“回收”它们。假设那些内容早于上次检查点的段文件不再需要,因此可以回收。
WAL归档的多种方式 #
在归档WAL数据时,需要在每个段文件填满后捕获其内容,并在该段文件被回收重新使用之前将数据保存到某个地方。根据应用场景和硬件配置的不同,“保存数据到某个地方”的方式可能有很多种:
- 可以将段文件复制到另一台机器上NFS挂载的目录,写入磁带驱动器(确保能够识别每个文件的原始名称),
- 或将它们批量整理并刻录到光盘上,
- 或者其他任何方式。
为了给数据库管理员提供灵活性,PostgreSQL尽量不假设归档的具体方式。相反,PostgreSQL允许管理员指定一个shell命令或一个归档库,用来执行将已完成的段文件复制到目标位置的操作。这个命令可以是简单的使用cp
的shell
命令,也可以是调用复杂C函数的命令,这完全由我们决定。
WAL归档的配置 #
要启用WAL归档,需要将wal_level
配置参数设置为replica
或更高,archive_mode
设置为on
,并在archive_command
配置参数中指定要使用的shell命令,或者在archive_library
配置参数中指定要使用的库。实际上,这些设置通常会放置在 postgresql.conf
文件中。
可以通过SQL查询来查看当前数据库的配置参数及其值。即SHOW命令查询这些配置的具体值:
1postgres=# SHOW wal_level;
2 wal_level
3-----------
4 replica
5(1 row)
6
7postgres=# SHOW archive_mode;
8 archive_mode
9--------------
10 off
11(1 row)
12
13postgres=# SHOW archive_command;
14 archive_command
15-----------------
16 (disabled)
17(1 row)
18
19postgres=# SHOW archive_timeout;
20 archive_timeout
21-----------------
22 1min
23(1 row)
归档命令archive_command
#
在archive_command
中,%p
会被替换为要归档的文件的路径名,而 %f
则被替换为文件名(路径名相对于数据目录)。如果需要在命令中嵌入实际的%
字符,则使用%%
。最简单的有用命令类似于:
1archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' # Unix
2archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"' # Windows
test ! -f /path/to/file
用于检查文件是否不存在。具体来说:
test
是一个用于条件测试的命令!
表示"非",即取反-f
是一个测试选项,用于检查文件是否存在且为普通文件
test ! -f /path/to/file
的含义是:
- 如果文件不存在,则返回真(true)
- 如果文件存在,则返回假(false)
test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f
中:
- 它会先检查目标目录中是否已存在同名文件
- 如果不存在同名文件,就执行复制操作
这是一种常见的防止覆盖已存在文件的备份或复制方法。
test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f
将把可归档的WAL段复制到目录/mnt/server/archivedir
。在替换了 %p 和 %f 参数后,实际执行的命令可能如下所示:
1test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_wal/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065
每生成一个新的要归档的文件,都会生成一个类似的命令。
归档命令将在与PostgreSQL服务器运行用户相同的用户权限下执行。由于被归档的WAL文件实际上包含了数据库中的所有数据,因此需要确保归档的数据不被未授权访问;例如,将其归档到没有组或全局读取权限的目录中。
重要的是,只有在归档命令成功时,它才应返回零的退出状态。收到零结果时,PostgreSQL会假定文件已成功归档,并会将其删除或回收。然而,非零状态告诉PostgreSQL文件未归档;它会定期重试直到成功。
归档模块archive_library
#
另一种归档方式是使用自定义归档模块作为archive_library
。由于这些模块是用C语言编写的,创建自己的模块可能比编写一个shell命令需要更多的工作。然而,归档模块比shell归档性能更高,并且可以访问许多有用的服务器资源。有关归档模块的更多信息,请参阅这里。
pg_stat_archiver
视图
#
pg_stat_archiver
是PostgreSQL 提供的一个视图,用于显示与归档WAL相关的统计信息。
1postgres=# SELECT * FROM pg_stat_archiver;
2 archived_count | last_archived_wal | last_archived_time | failed_count | last_failed_wal | last_failed_time | stats_reset
3----------------+--------------------------+-------------------------------+--------------+-----------------+------------------+-------------------------------
4 53 | 000000010000000000000035 | xxxx-xx-xx 17:44:22.322222+08 | 0 | | | xxxx-xx-xx 09:48:59.128683+08
归档进程和错误处理 #
当归档命令因信号(例如用于服务器关闭的SIGTERM信号以外的信号)或由于shell错误导致退出状态大于125(例如命令未找到)而终止时,或者归档功能发出ERROR或FATAL错误,归档进程将中止并由主进程重启。在这种情况下,pg_stat_archiver
中不会报告失败。
归档命令和库通常应该设计为拒绝覆盖任何现有的归档文件。这是一个重要的安全功能,可以防止管理员错误(例如将来自两个不同服务器的输出发送到同一归档目录)破坏归档的完整性。建议测试所提出的归档库,以确保它不会覆盖现有文件。
在极少数情况下,PostgreSQL可能会尝试重新归档一个已归档的WAL文件。例如,如果系统在服务器未能持久化归档成功记录前崩溃,则服务器在重新启动后会尝试重新归档该文件。当归档命令或库遇到一个现有文件时,如果WAL文件的内容与现有归档相同,并且现有归档已经完全持久化到存储中,则应返回零状态或true。如果现有文件与正在归档的WAL文件内容不一致,则归档命令或库必须返回非零状态或false。
前面的Unix示例命令通过包括一个单独的测试步骤来避免覆盖现有归档。在某些Unix平台上,cp 有类似-i
的选项可以做到这一点,但在没有验证返回正确退出状态的情况下不应依赖它们。(特别是,GNU cp在使用-i
时,如果目标文件已存在,则会返回零状态,而这不是期望的行为。)
在设计归档设置时,需考虑如果归档命令或库因某些方面需要操作员干预或归档空间耗尽而反复失败时会发生什么情况。例如,如果在没有自动换带器的情况下写入磁带,当磁带填满时,直到换带之前无法归档更多数据。应确保任何错误情况或对人工操作员的请求能够得到适当报告,以便尽快解决问题。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操作。可能希望将配置文件保存在常规文件系统备份程序会备份的位置。有关如何重新定位配置文件,请参见这里。
归档命令或函数仅在WAL段文件完成时调用。因此,如果服务器生成的WAL流量较少(或在某些时段较为松散),可能会导致事务完成到安全归档存储之间有较长延迟。为了限制未归档数据的存留时间,您可以设置archive_timeout
,强制服务器至少在那么长时间内切换到一个新的WAL段文件。请注意,由于强制切换而提前归档的文件仍与完全填满的文件相同长度。因此,不建议设置过短的archive_timeout
— 这会占用的归档存储。通常,设置一分钟左右的archive_timeout
是合理的。
此外,还可以使用pg_switch_wal
强制切换段文件,以确保刚完成的事务尽快归档。与WAL管理相关的其他实用功能列在这里的表中。
配置细节与限制 #
当wal_level
设置为minimal
时,一些SQL命令会优化以避免WAL日志记录,参考这里。如果在执行这些语句期间启用了归档或流复制,则WAL中将不会包含足够的恢复归档信息。(崩溃恢复不受影响。)因此,wal_level
只能在服务器启动时更改。然而,archive_command
和archive_library
可以通过重新加载配置文件进行更改。如果正在通过shell进行归档,并希望暂时停止归档,可以将archive_command
设置为空字符串(’’)。这将导致WAL文件在pg_wal/
中积累,直到重新建立有效的 archive_command
。
进行基础备份 #
执行基础备份最简单的方法是使用pg_basebackup
工具。它可以将基础备份创建为常规文件或tar文件。如果需要比pg_basebackup
更大的灵活性,还可以使用低级API执行基础备份(参见使用低级api进行基础备份)。
在执行基础备份时,不必过于关注所需的时间。然而,如果服务器通常在禁用full_page_writes
的情况下运行,可能会注意到在备份过程中性能有所下降,因为在备份模式下,full_page_writes
会被强制启用。
要使用备份,需要保留在文件系统备份期间和之后生成的所有WAL文件。为帮助完成此操作,基础备份过程会创建一个备份历史文件,并立即将其存储到WAL存档区域。该文件的名称基于需要的第一个WAL文件。例如,如果起始WAL文件是0000000100001234000055CD
,那么备份历史文件会命名为0000000100001234000055CD.007C9330.backup
。(文件名的第二部分表示WAL文件中的确切位置,通常可以忽略。)一旦安全地归档了文件系统备份以及在备份过程中使用的WAL文件(如备份历史文件中所指定),所有编号较小的已归档WAL文件不再需要用于恢复文件系统备份,可以删除。但是,应考虑保留多个备份集,以确保可以绝对恢复您的数据。
备份历史文件只是一个小文本文件。它包含提供给pg_basebackup的标签字符串,以及备份的开始和结束时间及WAL文件。如果使用标签来标识相关的转储文件(dump file),那么归档的历史文件足以告诉您要恢复哪个转储文件(dump file)。
由于必须保留从上次基础备份开始的所有已归档WAL文件,因此基础备份之间的间隔通常应根据我们愿意为已归档WAL文件分配多少存储空间来选择。另外还应考虑在需要恢复时,愿意花费多少时间进行恢复——系统必须重放所有这些WAL文件,如果自上次基础备份以来已经过了很长时间,恢复可能需要较长时间。
pg_basebackup
工具使用
#
1mkdir basebak
2pg_basebackup -l "Daily Backup" -Ft -D ./basebak
3ls ./basebak/
4backup_manifest base.tar pg_wal.tar
在WAL归档目录中创建了一个备份历史文件00000001000000000000003A.00000028.backup
。
使用低级API进行基础备份 #
使用低级API进行基础备份的过程比pg_basebackup
方法多了一些步骤,但相对简单。非常重要的是,必须按顺序执行这些步骤,并在进行下一步之前验证每个步骤是否成功。
多个备份可以同时运行(包括使用此备份API启动的备份和使用 pg_basebackup
启动的备份)。
确保已启用并正常运行WAL存档。
以具有运行
pg_backup_start
权限的用户身份连接到服务器(无需指定数据库),然后执行以下命令:1SELECT 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
)。在此过程中,既不需要也不建议停止数据库的正常操作。在之前的连接中执行以下命令:
1SELECT * 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
,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 文件,因此备份不完整,无法恢复。
pg_backup_start和pg_backup_stop
这两个函数
pg_backup_start()
和pg_backup_stop()
实际上并不直接创建备份文件。它们是低级别的备份控制函数,主要用于准备和结束在线备份(也称为热备份)。真正的备份文件通常需要另外的步骤来创建,比如:
- 使用文件系统级别的备份工具
- 手动复制数据目录
在这些函数的使用过程中:
pg_backup_start()
会准备数据库进入备份模式pg_backup_stop()
会结束备份模式而
pg_basebackup
不需要执行pg_start_backup
和pg_stop_backup
, 因为命令中已经封装了这些操作.
备份数据目录 #
某些文件系统备份工具在尝试复制的文件在复制过程中发生变化时,会发出警告或错误。当对活动数据库进行基础备份时,这属于正常情况,并非错误。然而,需要确保能够区分此类警告与真实错误。例如,某些版本的rsyn 会为“源文件消失”返回一个单独的退出码,可以编写一个驱动脚本,将此退出码视为非错误情况。此外,某些版本的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_wal/
中的任何文件;这些文件来自文件系统备份,因此可能是过时的,而不是当前的。如果完全没有归档pg_wal/
,则应重新创建它,并确保具有正确的权限。如果之前将其设置为符号链接,则要小心重新建立符号链接。 - 如果在步骤2中保存了未归档的WAL段文件,请将它们复制到
pg_wal/
中。(最好是复制,而不是移动,这样在出现问题并且需要重新开始时,仍然可以保留未修改的文件。) - 在
postgresql.conf
中设置恢复配置(参见这里),并在集群数据目录中创建一个recovery.signal
文件。可能还需要临时修改pg_hba.conf,以防止普通用户连接,直到确认恢复成功为止。 - 启动服务器。服务器将进入恢复模式,并继续读取所需的归档WAL文件。如果由于外部错误导致恢复被中断,服务器可以简单地重新启动并继续恢复。恢复过程完成后,服务器将删除
recovery.signal
文件(以防止意外重新进入恢复模式),然后开始正常的数据库操作。 - 检查数据库内容,以确保已恢复到所需的状态。如果没有,返回第1步。如果一切正常,可以恢复
pg_hba.conf
至正常状态,允许用户连接。
恢复过程中最关键的部分是设置恢复配置,描述希望如何恢复以及恢复的进度。必须指定的唯一一项配置是restore_command
,它告诉PostgreSQL如何检索归档的WAL文件段。像 archive_command
一样,这是一个 shell 命令字符串。它可以包含%f
,它会被期望的WAL文件的名称替代,和%p
,它会被复制WAL文件的路径替代。(该路径名是相对于当前工作目录,即集群的数据目录。)如果需要在命令中嵌入实际的%
字符,请写%%
。最简单有效的命令是类似这样的:
1restore_command = 'cp /mnt/server/archivedir/%f %p'
它将从目录/mnt/server/archivedir
中复制先前归档的WAL段。当然,可以使用更复杂的命令,甚至是一个shell脚本。
命令在失败时返回非零退出状态非常重要。当请求归档中不存在的文件时,命令必须返回非零状态,这并不算作错误条件。唯一的例外是,如果命令因信号(除SIGTERM外,SIGTERM 用于数据库服务器关闭)或shell错误(例如命令未找到)而终止,那么恢复将中止,服务器将无法启动。
并非所有请求的文件都是WAL段文件;还可能会遇到后缀为.history
的文件请求。请注意,%p
路径的基本名称与%f
不同;不要期望它们可以互换。
找不到的WAL段将会在pg_wal/
中查找,这允许使用最近未归档的段。优先使用归档中的WAL
段,而不是pg_wal/
中的文件。
恢复过程中,系统将遍历所有可用的WAL段,从而将数据库恢复到当前时间点(或者尽可能接近当前时间点,具体取决于可用的WAL段)。因此,正常的恢复将以“文件未找到”的消息结束,错误消息的具体文本取决于所使用的restore_command
。还可能在恢复开始时看到类似00000001.history
的文件错误消息,这在简单恢复情况下是正常的,并不表示问题。有关详细信息,请参见时间线。
如果想恢复到某个特定的时间点(例如,恢复到初级DBA删除主要事务表之前),只需指定相应的停止点。你可以通过日期/时间、命名恢复点,或者某个特定事务ID的完成来指定停止点,这称为“恢复目标”。截至目前,日期/时间和命名恢复点选项更为常用,因为目前没有工具能够帮助你准确识别需要使用的事务ID。
注意
停止点必须在基础备份的结束时间之后,即在
pg_backup_stop
的结束时间之后。不能使用某个基础备份恢复到该备份正在进行时的时间点。(要恢复到这样的时间点,必须回到先前的基础备份并从那里开始。)
如果恢复过程中发现损坏的WAL数据,恢复将停止,服务器无法启动。在这种情况下,可以从头重新运行恢复过程,并指定一个损坏点之前的“恢复目标”,以便恢复能够正常完成。如果恢复因外部原因失败,例如系统崩溃或 WAL 存档变得不可访问,则可以简单地重新启动恢复,恢复将从几乎失败的位置重新开始。恢复的重启过程类似于正常操作中的检查点:服务器会定期将所有状态强制写入磁盘,然后更新 pg_control
文件,以表明已处理的WAL数据无需再次扫描。
时间线 #
将数据库恢复到先前某个时间点的能力带来了类似科幻小说中关于时间旅行和平行宇宙的复杂性。例如,在数据库的原始历史中,假设你在星期二晚上5:15删除了一个关键表格,但直到星期三中午才意识到错误。你毫不在意,恢复了备份,将数据库恢复到星期二晚上5:14的时间点,恢复正常。在这个数据库的历史中,你从未删除过表格。然而,假设你后来意识到这个决定不理想,想恢复到星期三早上的某个时间点。如果在数据库恢复期间,它覆盖了部分WAL段文件,而这些文件是通向你希望恢复的时间点的,那么你将无法恢复。为了避免这种情况,你需要将恢复后生成的WAL记录与原始历史中的记录区分开来。
为了解决这一问题,PostgreSQL引入了**时间线(timelines)**的概念。每当归档恢复完成时,都会创建一个新的时间线,以标识恢复后生成的WAL记录系列。时间线ID是WAL段文件名的一部分,因此新时间线不会覆盖旧时间线生成的WAL数据。例如,在WAL文件名0000000100001234000055CD
中,前面的00000001
是时间线 ID(以十六进制表示)。(请注意,在其他地方,比如服务器日志消息中,时间线ID通常是十进制。)
实际上,可以归档多个不同的时间线。虽然这看起来可能没什么用,但它在很多情况下非常有用。假设你不确定恢复到哪个时间点,因此需要通过反复试验进行几次点时间恢复,直到找到最合适的分支点。如果没有时间线,这个过程会迅速变得混乱。而有了时间线,你可以恢复到任何先前的状态,包括那些你放弃过的时间线分支。
每次创建新时间线时,PostgreSQL会创建一个“时间线历史”文件,记录它从哪个时间线分支出来以及分支的时间。这些历史文件对于恢复时正确选择WAL段文件非常重要,尤其是在包含多个时间线的归档中。因此,它们会像WAL段文件一样被归档到WAL归档区域。由于历史文件只是小型文本文件,所以存储成本低,适合长期保留(与大型段文件不同)。如果需要,还可以在历史文件中添加注释,记录时间线创建的过程和原因。这样的注释对于处理多个时间线时尤其有用。
恢复的默认行为是恢复到归档中找到的最新时间线。如果希望恢复到基础备份时的时间线或某个特定的子时间线(即,恢复到某个恢复后生成的状态),需要在recovery_target_timeline
中指定目标时间线ID或当前时间线。不能恢复到早于基础备份的时间线。
提示与示例 #
这里提供了一些配置连续归档的提示和示例。
独立热备份 #
可以使用PostgreSQL的备份功能生成独立热备份。这些备份无法用于点时间恢复,但通常比pg_dump
备份更快速,备份和恢复速度通常要快得多。然而,它们比pg_dump
备份要大得多,因此在某些情况下,速度优势可能会消失。
与基础备份一样,生成独立热备份最简单的方法是使用pg_basebackup
工具。如果在调用时包含-X
参数,则所有需要的WAL都会自动包含在备份中,恢复备份时无需采取特别的措施。
压缩归档日志 #
如果归档存储空间是一个问题,可以使用gzip压缩归档文件:
1archive_command = 'gzip < %p > /mnt/server/archivedir/%f.gz'
然后,在恢复时需要使用gunzip:
1restore_command = 'gunzip < /mnt/server/archivedir/%f.gz > %p'
archive_command脚本 #
许多人选择使用脚本来定义archive_command
,以使其postgresql.conf
配置看起来更简单:
1archive_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被重放到同一台机器上,但重放到一个新的数据目录中,这也可能是危险的:重放操作仍会覆盖原表空间的内容。为了避免类似的问题,最佳做法是在创建或删除表空间后,重新进行基备份。
还应注意,默认的WAL格式相当庞大,因为它包含了许多磁盘页面快照。这些页面快照是为了支持崩溃恢复,因为我们可能需要修复部分写入的磁盘页面。根据系统的硬件和软件配置,部分写入的风险可能足够小以忽略不计,这时可以通过关闭full_page_writes
参数来显著减少归档WAL文件的总量。(在执行此操作之前,请参考这里。)关闭页面快照并不妨碍将WAL用于点时间恢复(PITR)操作。未来的一个开发方向是通过删除不必要的页面副本来压缩归档的WAL数据,即使在启用full_page_writes
的情况下也能实现。目前,管理员可以通过尽可能增大检查点间隔参数来减少WAL中包含的页面快照数量。