在线备份以及即时恢复(PITR)

PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 24. 备份与恢复 | 下一页

# 24.3. 在线备份以及即时恢复(PITR)

在任何时候，PostgreSQL都在集群的数据目录的`pg_xlog/` 子目录里维护着一套预写日志(WAL)。 这些日志记录着每一次对数据库的修改细节。 这些日志存在是为了防止崩溃： 如果系统崩溃，数据库可以通过"重放"上次检查点以来的日志记录以恢复数据库的完整性。 但是，日志的存在让它还可以用于第三种备份数据库的策略： 我们可以组合文件系统备份与WAL文件的备份。如果需要恢复，我们就恢复备份， 然后重放备份了的WAL文件，把备份恢复到当前的时间。这个方法对管理员来说， 明显比以前的方法更复杂，但是有非常明显的优势：

- 在开始的时候我们不需要一个非常完美的一致的备份。 任何备份内部的不一致都会被日志重放动作修改正确 (这个和崩溃恢复时发生的事情没什么区别)。因此我们不需要文件系统快照的功能， 只需要tar或者类似的归档工具。

- 因为我们可以把无限长的WAL文件序列连接起来， 所以连续的备份简化为连续地对WAL文件归档来实现。 这个功能对大数据库特别有用，因为大数据库的全备份可能并不方便。

- 我们可没说重放WAL记录的时候我们必须重放到结尾。 我们可以在任意点停止重放，这样就有一个在任意时间的数据库一致的快照。 因此，这个技术支持即时恢复： 我们可以把数据库恢复到你开始备份以来的任意时刻的状态。

- 如果我们持续把WAL文件序列填充给其它装载了同样的基础备份文件的机器， 我们就有了一套热备份系统：在任何点我们都可以启动第二台机器， 而它拥有近乎当前的数据库拷贝。

> 注意: pg_dump和 pg_dumpall没有产生文件系统级别备份，并且不能作为 连续归档解决方案的一部分。比如备份是符合逻辑的并且不包含 WAL重放使用的足够信息。

和简单的文件系统备份技术一样，这个方法只能支持整个数据库集群的恢复， 而不是一个子集。同样，它还要求大量的归档存储：基础备份量可能很大， 而且忙碌的系统将生成许多兆需要备份的的WAL流量。但是， 它仍然时在需要高可靠性的场合下的最好的备份技术。

要想从连续归档中成功恢复（也被许多数据库供应商称为"在线备份"）， 你需要一套连续的WAL归档文件， 它们最远回朔到你开始备份的时刻。因此，要想开始备份， 你应该在开始第一次基础备份之前根据我们讨论过的归档WAL文件机制设置并测试你的步骤。

## 24.3.1. 设置WAL归档

抽象来看，一个运行着的PostgreSQL系统生成一个无限长的WAL日志序列。 系统物理上把这个序列分隔成WAL段文件，通常每段16M(在编译PostgreSQL的 时候可以改变其大小)。这些段文件的名字是数值命名的，这些数值反映他们在抽取出来的 WAL 序列中的位置。在不适用WAL归档的时候，系统通常只是创建几个段文件然 后"循环"使用它们，方法是把不再使用的段文件的名字重命名为更高的段编号。 系统假设那些内容比前一次检查点更老的段文件已经没用了，然后就可以循环利用。

在归档WAL数据的时候，我们希望在每个段文件填充满之后捕获之， 并且把这些数据在段文件被循环利用之前保存在某处。 根据应用以及可用的硬件的不同，我们可以有许多不同的方法"把数据保存在某处"： 我们可以把段文件拷贝到一个 NFS 挂载的目录，把它们放到另外一台机器上， 或者把它们写入磁带机里(需要保证你有办法把文件恢复为原名)，或者把它们打成包， 烧录到 CD 里，或者是其它的什么方法。为了给数据库管理员提供最大可能性的灵活性， PostgreSQL试图不对如何归档做任何假设。取而代之的是， PostgreSQL让管理员声明一个shell命令执行来拷贝一个完整的段文件到它需要去的地方。 该命令可以简单得就是一个`cp`，或者它可以调用一个复杂的shell脚本— 这些都由管理员决定。

为了启动WAL归档，设置wal_level 配置参数到`archive`或者更高, archive_mode为`on`， 并且所使用的shell命令由配置参数archive_command声明， 它实际上总是放在`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_xlog/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065

为每一个归档的新文件产生类似的命令。

归档命令将在运行PostgreSQL服务器的同一个用户的权限下执行。 因此被归档的WAL文件实际上包含你的数据库里的所有东西， 所以你应该确保自己的归档数据不会被别人窥探；比如， 归档到一个没有组或者全局读权限的目录里。

有一点很重要：当且仅当归档命令成功时，它才返回零。 在得到一个零值结果之后，PostgreSQL将假设该WAL段文件已经成功归档， 因此它稍后将被删除或者被新的数据覆盖。但是， 一个非零值告诉PostgreSQL该文件没有被归档； 因此它会周期性的重试直到成功。

归档命令通常应该设计成拒绝覆盖已经存在的归档文件。 这是一个非常重要的安全特性，可以在管理员操作失误 (比如把两个不同的服务器的输出发送到同一个归档目录)的时候保持你的归档的完整性

我们建议你首先要测试你准备使用的归档命令，以保证它实际上不会覆盖现有的文件， 并且在这种情况下它返回非零状态。 上边Unix例子中的命令包含单独的`测试`步骤。在一些Unix平台上，`cp` 有可以使用的开关比如`-i`可以做同样的简单的事情。 但是你不应该依靠这些而不验证返回的正确退出状态。 （尤其是，当使用`-i`并且已经存在目标文件时，GNU `cp`将返回状态零， 这不是期望的操作。）

在设计你的归档环境的时候，请考虑一下如果归档命令不停失败会发生什么情况， 因为有些方面要求操作者的干涉，或者是归档空间不够了。比如， 如果你往磁带机上写，但是没有自动换带机，那么就有可能发生这种情况； 如果磁带满了，那就除非换磁带，否则啥事也做不了。 你应该确保任何错误条件或者要求操作员干涉的错误都会正确报告， 这样才能迅速解决这些问题。否则`pg_xlog/`目录会不停地填充WAL段文件， 直到问题解决。（如果文件系统由`pg_xlog/`填充，PostgreSQL将执行 PANIC关机。没有提交的事务将丢失，但是数据库将保持未连接直到你释放一些空间）。

归档命令的速度并不要紧，只要它能跟上你的服务器生成WAL数据的平均速度即可。 即使归档进程落在了后面一点，正常的操作也会继续进行。如果归档进程慢很多， 就会增加灾难发生时的数据丢失量。同时也意味着`pg_xlog/` 目录包含大量未归档的日志段文件，并且可能最后超出了磁盘空间。 我们建议你监控归档进程，确保它是按照你的意识运转的。

在写自己的归档命令的时候，你应该假设被归档的文件最多 64个字符长并且可以包含ASCII字母、数字、点的任意组合。 我们不必要记住原始的全路径(`%p`)，但是有必要记住文件名(`%f`。

请注意尽管WAL归档允许你恢复任何对PostgreSQL数据库的修改， 在最初的基础备份之后，它还是不会恢复对配置文件的修改 (`postgresql.conf`, `pg_hba.conf`和 `pg_ident.conf`)， 因为这些文件都是手工编辑的，而不是通过SQL操作来编辑的。 所以你可能会需要把你的配置文件放在一个日常文件系统备份过程即可处理到的地方。 参阅第 18.2 节获取如何重定位配置文件的知识。

因为归档命令仅在已经完成的WAL段上调用，因此， 如果你的服务器只产生很小的WAL流量或段之间的间隔很长， 那么在事务完成之后与其被安全归档之间就会存在很长的延时。 为了限制未归档数据的最长期限，可以设置archive_timeout强制服务器在切换 WAL段之间的时间间隔。需要注意的是， 由于强制切换而提早结束的已归档文件的大小与完整的归档文件相同。 因此将`archive_timeout`设为一个很小的值是不明智的— 它将很快耗尽归档空间。将`archive_timeout`设置为60秒左右通常是比较合理的。

同样，如果你想确保刚刚完成的事务被立即归档， 那么也可以通过`pg_switch_xlog`手动强制切换段文件。 其它与WAL管理相关的工具函数在表 9-65中列出。

当`wal_level`是`minimal`的时候，一些SQL命令进行优化以避免WAL日志， 正如第 14.4.7 节所描述的。 如果在这些语句之一执行过程中打开归档或者流复制，WAL不包含归档恢复的足够信息。 (崩溃恢复不受影响)，出于这些原因，在服务器开始改变`wal_level`。 然而，重新加载配置文件时改变`archive_command`。 如果你希望暂时停止归档，这样做的一个方法是设置`archive_command`为空字符串 (`''`)。这将导致WAL文件在`pg_xlog/`中积累直到`archive_command`重新建立。

## 24.3.2. 进行一次基础备份

执行基础备份最简单方式是使用pg_basebackup工具， 它使用普通文件或者tar归档创建基础备份。 如果比pg_basebackup提供更多的灵活性，你也可以使用 低水平API（参阅第 24.3.3 节）创建基础备份。

不必担心基础备份需要大量时间。然而， 如果你正常运行禁用了`full_page_writes`的服务器， 当运行备份时，你可能注意到性能方面， 因为`full_page_writes`有效强加在备份方式中。

要使用这个备份，你需要保存所有备份开始以及之后的WAL段文件。 为了帮助你实现这个任务，基础备份过程创建一个备份历史文件， 它马上存储到WAL归档区域。这个文件的名字是以你在使用文件系统备份的时候需要的第一个 WAL段文件的名字命名的。比如，如果开始 WAL 文件是`0000000100001234000055CD`， 那么备份历史文件将命名为类似`0000000100001234000055CD.007C9330.backup`这样的东西。 这个文件名的第二部分表示在该WAL文件里面的准确位置，通常可以被忽略。 一旦你安全地把这些日志段文件归了档， 那么你就可以删除所有那些数值名字在这个文件前面的归档的WAL段。 文件系统备份不再需要它们了。当然，你应当保留几套备份以绝对确保可以恢复先前的数据。

备份历史文件只是一个小的文本文件。它包含你给予pg_basebackup的标签字符串， 以及备份的起始时间和终止时间。如果你使用这个标签来表示转储文件放在哪里， 则在需要的时候，归档的历史文件就足够告诉你转储文件存放在哪里了。

因为你必须保留直到最后一次基础备份的所有归档的WAL文件， 那么两次基础备份之间的间隔通常是根据你想在归档WAL 文件上花多少存储空间来定的。 你还应该考虑你准备在恢复上花多少时间。如果需要恢复的话— 系统将需要重放所有那些段， 而如果最后一次基础备份以来，时间已经很长了， 那么那些动作可能会花掉好些时间。

## 24.3.3. 使用低级别API进行基础备份

使用包含多个步骤的低水平API而不是pg_basebackup方法， 但是相对简单。在序列中执行这些步骤非常重要，并且在进行下一步之前需要验证当前步成功。

1. 确保WAL归档打开并且可以运转。

2. 以数据库超级用户身份连接到数据库，发出命令:

       SELECT pg_start_backup('label');

   这里的`label`是任意你想使用的这次备份操作的唯一标识 (一个好习惯是使用备份转储文件的放置地全路径)。 `pg_start_backup`用备份信息在集群目录里 创建一个备份标签文件`backup_label`。 包含起始时间和标签字符串。该文件对于备份完整性是非常重要的，你需要从中恢复。

   至于你连接到集群中的那个数据库没什么关系。 你可以忽略函数返回的结果；但是如果它报告错误， 那么在继续之前先处理它。

   默认情况下，`pg_start_backup`可能需要很长的时间才能完成。 这是因为它会执行一个检查点，并且I/O所需的检查点会被分散在一个显著的时间段， 默认情况下，使用一半你的相互检查点间隔（参见配置参数 checkpoint_completion_target）。 这是你想要的，因为它最大限度地减少对查询处理的影响。 如果你想尽快开始备份，使用：

       SELECT pg_start_backup('label', true);

   这强制检查点尽快完成。

3. 执行备份，使用任何方便的文件系统工具，比如tar或者cpio （而不是pg_dump或者 pg_dumpall）。 这些操作过程中既不需要关闭数据库，也不需要关闭数据库的操作。

4. 再次以数据库超级用户身份连接数据库，然后发出命令：

       SELECT pg_stop_backup();

   这将中止备份模式并自动切换到下一个WAL段。 自动切换是为了在备份间隔中写入的最后一个WAL 段文件可以立即为下次备份作好准备。

5. 只要在备份过程中使用的WAL段文件备份完毕，你的备份工作就完成了。 通过`pg_stop_backup`的结果标识的文件是需要形成一套完整备份文件的最后一段。 如果`archive_mode`已启用， `pg_stop_backup`不返回，直到最后段被归档。 这些文件的归档是自动发生的，因为已配置`archive_command`。 在大多数情况下，这将迅速发生，但建议您监控您的存档系统以确保没有延迟。 如果归档进程已经落后，因为存档命令失败，它会继续重试直到存档成功，备份完成。 如果您希望在执行`pg_stop_backup`时有时间限制，则设定适当的 `statement_timeout`值。

一些文件系统备份工具发出警告或错误， 如果这些文件他们正试图复制副本处理的变化。 当执行活动数据库的基础备份，这种情况是正常的并且不发生错误。 但是，你需要确保你能辨别这种从实际错误中的投诉。例如，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_xlog/`子目录。 这个略微复杂些的动作是值得的，因为它减少了恢复时候的错误。 如果`pg_xlog/`是一个指向集群目录之外的符号连接， 那么这件事情很容易处理，出于性能考虑的时候经常这么做。 你可能还想排除`postmaster.pid`和`postmaster.opts`， 记录有关运行postmaster的信息，而不谈postmaster。 它最终将使用这个备份。（这些文件可以混淆pg_ctl）。

通常忽略集群的`pg_replslot/`目录中的备份转储文件也是一个好主意， 所以存在于主机上的复制槽没有成为备份的一部分。否则， 后续使用该备份创建一个备用机，可能会导致在备用机上无期限保留WAL文件， 并且如果启用了热备份反馈，可能导致主机膨胀， 因为使用这些复制槽的客户端仍然将连接并且在主机上升级这些复制槽，而不是在备用机上。 即使备份只是为了用于创建一个新的主机，拷贝复制槽并没什么特别的用处， 因为这些复制槽的内容将很有可能在新主机上线时已经非常过时了。

还有一件事值得一提，那就是`pg_start_backup`函数在数据库集群目录里创建了 一个叫`backup_label`的文件，它被`pg_stop_backup`删除。 这个文件当然也会作为备份转储文件的一部分归档。 这个备份标签文件包含你给予`pg_start_backup`的标签字符串， 以及`pg_start_backup`运行的时刻，以及起始WAL文件的名字。 如果有混淆，那么我们可以看看备份转储文件里面然后 判断转储文件来自那个备份会话。 然而，这些文件不仅仅是你的信息，它的存在和内容对系统的恢复过程的正确操作是关键的。

我们还可以在服务器停止的时候制作一个备份转储。 在这种条件下，很明显你不能使用`pg_start_backup` 或者`pg_stop_backup`， 并且因此你必须靠自己的手段来跟踪备份转储文件都是那些， 以及相关的WAL文件最远走到哪里。 通常使用上面的在线备份步骤更好些。

## 24.3.4. 从在线备份中恢复

好，最糟糕的事情发生了，现在你需要从备份中恢复。下面是步骤：

1. 停止服务器，如果它还在运行的话。

2. 如果你还有足够的空间，把整个集群数据目录和所有表空间拷贝到一个临时位置， 以防万一你之后还需要它们。请注意这个预防措施要求你在系统里有足够的剩余 空间来现有库的保持两份拷贝。如果你没有足够的空间， 那么你至少需要把集群数据目录的`pg_xlog`子目录的内容拷贝到安全的地方， 因为它们可能包含系统宕掉的时候还没有归档的日志。

3. 然后清理掉所有在该集群数据目录里的现存文件， 以及所有你使用的表空间里根目录下的现存文件。

4. 从你的备份转储中恢复数据库文件。 要小心用正确的所有者(数据库系统用户，而不是`root`!)和权限恢复它们。 如果你使用了表空间，你可能需要核实在`pg_tblspc/`里的符号连接都得到正确恢复。

5. 删除任何目前还在`pg_xlog/`里的文件；这些文件来自备份转储， 因此它们可能比目前的老。如果你就根本没有归档`pg_xlog/`，那么重建之， 它具有合适的权限，如果你已经像从前那样建立了，小心确保你重新建立它作为符号链接，

6. 如果你有在步骤 2 里面保存的WAL段文件，那么把它们拷贝到`pg_xlog/`。 最好是拷贝它们，而不是把它们移动回来，这样即使发生了糟糕的事情，你需要重启的时候， 你也依然拥有未修改的文件。

7. 在集群数据目录里创建一个恢复命令文件`recovery.conf`(参阅第 26 章)。 你可能还需要临时修改`pg_hba.conf`以避免普通用户连接， 直到你确信恢复已经正常了为止。

8. 启动服务器。服务器将进入恢复模式并且继续读取它需要的 WAL 归档文件。 在遇见外部错误的应当中止恢复过程，然后重新启动服务器， 这样它会自动继续进行恢复工作。在恢复过程完成后， 服务器将把`recovery.conf`改名为`recovery.done` 以避免不小心因后面的崩溃再次进入恢复模式， 然后开始正常的数据库操作。

9. 检查数据库的内容以确保你已经恢复到你期望的位置。 如果还没有， 回到步骤1。如果全部正常，则恢复`pg_hba.conf` 成正常状态以允许普通用户登录。

所有这些操作的关键是设置一个恢复命令文件， 这个文件描述你希望如何恢复以及恢复应该走到哪里。 你可以使用`recovery.conf.sample`(通常安装在安装目录的`share/` 子目录里)作为原型。你必须在`recovery.conf`里面声明的一个东西是`restore_command`， 它告诉系统如何拿回归档的WAL文件段。类似`archive_command`， 这个是一个脚本命令字符串。它可以包含`%f`，这个变量会被需要的日志文件名替换， 以及`%p`，它会被要拷贝去的日志文件的绝对路径代替。 如果需要在命令里替换真正的`%`字符，那么就双写(`%%`)。 最简单的有用命令是类似下面的东西：

    restore_command = 'cp /mnt/server/archivedir/%f %p'

这个命令将把以前归档的WAL段从`/mnt/server/archivedir`目录拷贝过来。 你当然可以使用某些更复杂的东西，甚至是一个要求操作者挂载合适的磁带的shell脚本。

重要的一点是：该命令在失败的时候返回非零值。如果日志文件没有出现在规档中， 那么将调用该命令请求没有在归档中出现的文件；在问到的时候， 它必须返回非零。这个不是错误条件。一个例外是，如果该命令被一个信号 （除了SIGTERM，它用作数据库服务器关闭的一部分） 或者shell的一个错误（比如没有找到命令）终止，那么将退出恢复， 并且服务器将不会启动。

并不是所有需要的文件都是WAL分段文件；你应该希望请求`.backup`或者 `.history`的后缀文件。 还要注意`%p`路径的基础名将和`%f`不一样；不要认为它们是可以互换的。

在归档中没有的WAL分段将在`pg_xlog/`中； 这样就允许使用最近没有归档的段。但是在归档中的段将比`pg_xlog/`中的优先。

通常，恢复将处理所有可用的 WAL 段， 因此将把数据库恢复到当前时间(或者是在所给出的可用 WAL 段数的情况下， 我们能走到的最近的地方)。 因此，一个正常恢复以"文件没找到"消息结束， 错误消息的确切文本依赖于`restore_command` 你的选择。你也可能在类似于`00000001.history`文件恢复开头看到错误消息。 这也是正常的，并不表明简单恢复情况下的问题。参阅第 24.3.5 节 获取更多信息。

但是如果你想恢复到某些以前的时刻点(比如， 在菜鸟DBA删除你的主要事务表之前)， 那么只需要在`recovery.conf`里声明要求的停止点。 你可以通过日期/时间来声明，也可以通过特定事务ID的结束来声明这个停止点， 我们叫做"恢复目标"。目前，只有日期/时间和称为恢复点选项比较有用， 因为我们没有工具来帮助你精确地标识应该使用哪个事务ID。

> 注意: 请注意停止点必须在备份的终止时间之后(也就是`pg_stop_backup`的时间)。 你无法使用一个基础备份恢复到备份正在进行中的某个时刻。要想恢复到该时刻， 你必须回到你以前的基础备份，然后从那个位置向前滚动。

如果在恢复过程中发现在 WAL 数据中存在错误，那么恢复将在错误的地方停止， 并且不会启动服务器。在这种情况下，可以指定一个位于错误点之前的"恢复目标"， 然后从起始点开始重新运行恢复进程，这样恢复就可以正常完成。 如果由于外部原因(系统崩溃、无法读取 WAL 归档)导致恢复失败， 那么可以简单的重新启动恢复过程即可，它将从上次失败的地方继续。 重新启动恢复过程与检查点的操作非常类似： 服务器周期性的强制将其状态记录到磁盘上并更新`pg_control`文件以标识已经处理的 WAL 数据不需要被再次扫描。

## 24.3.5. 时间线

能够把数据库恢复到以前的某个时间点的能力导致了一些类似科幻小说里的时间跟踪 和并行宇宙这样的复杂情况。在数据库最初的历史里， 可能你在周二下午5:15删除掉了一个非常关键的表。但是没有意识到你自己的错误直到周三中午。 有条不紊地拿出备份，恢复到周二晚上5:14的即时备份。在这个数据库宇宙的历史里， 你从来没有删除过那个表。但是假如你后来认识到这么干是错误的， 并且想回到最初的历史中的稍后的点。你没法这么干，因为在数据库运行的时候， 它覆盖了一些WAL段文件的序列，这些序列就在你希望回去的区间里。 因此你的确需要区分在你从那些原始数据库历史生成的WAL中完成即时恢复之 后生成的WAL序列。

为了处理这些问题，PostgreSQL有个叫时间线的概念。 当完成一个归档恢复时，那么就创建一个新的时间线，以表示在该次恢复之后生成的 WAL 记录序列。 时间线ID号是WAL段文件名的一部分， 因此新的时间线并不会覆盖以前的时间线生成的 WAL 数据。 实际上我们可以归档许多不同的时间线。虽然这些看起来像没用的特性， 但它却可能常常是救命稻草。考虑一下你并不很确信应该恢复到那个时刻的情况， 这个时候你不得不做好几次试验性即时恢复然后从中找到旧历史中最好的分支。 如果没有时间线，那么这个过程可能很快就会导致无法管理的混乱。 有了时间线，你可以恢复到任意以前的状态， 包括恢复到你后来放弃的时间线分支的状态。

每当创建一个新的时间线的时候，PostgreSQL都创建一个"时间线历史"文件， 它显示自己从哪个时间线分出来，以及何时分出来的。 这些历史文件是在从包含多个时间线的归档中进行恢复时， 允许系统选取正确 WAL 段文件的必要文件。因此， 它们像 WAL 段文件一样归档到 WAL 归档里。 历史文件只是很小的文本文件(不像段文件很大)， 所以独立地保存他们代价很小，也值得做。如果你喜欢， 你可以在历史文件里加入注释，记录自己为什么设置这个时间线以及如何设置的等信息。 这样的注释会在你有厚厚一堆不同的时间线需要选择和分析的时候特别有价值。

恢复的缺省的行为是沿着与基础备份的同一个时间线恢复。 如果你想恢复到某些子时间线，也就是，你想回到某些本身就是在开始恢复之后发生的状态。 你需要在`recovery.conf`里声明目标时间线ID。 你无法恢复到比基础备份更早的时间线分支。

## 24.3.6. 技巧和例子

给出了配置连续归档的一些技巧。

### 24.3.6.1. 单机热备

可以使用PostgreSQL备份工具产生单机热备。这些都是不能使用时间点恢复的备份， 往往备份和恢复比pg_dump转储速度更快。 （他们也比pg_dump转储更大，因此在某些情况下速度优势可能是否定的 ）。

作为基础备份，最简单的方式是使用pg_basebackup工具产生单机热备份。 当调用它时，如果你有`-X`参数，那么所有需要使用备份的事务日志将包含在自动备份中， 并且不需要特殊操作恢复备份。

如果在复制备份文件中需要更大的灵活性，较低的水平过程可更好用于单机热备份。 为了准备低水平单机热备份，设置`wal_level`到 `archive`或者更高, `archive_mode`为 `on`，并且只有当开关文件存在时， 建立`archive_command`执行存档。例如：

    archive_command = 'test ! -f /var/lib/pgsql/backup_in_progress || (test ! -f /var/lib/pgsql/archive/%f && cp %p /var/lib/pgsql/archive/%f)'

当`/var/lib/pgsql/backup_in_progress`存在时，这个命令将执行归档。 否则默默返回零退出状态（允许PostgreSQL回收不需要的WAL文件）。

有了这个准备，备份可以使用如下脚本：

    touch /var/lib/pgsql/backup_in_progress
    psql -c "select pg_start_backup('hot_backup');"
    tar -cf /var/lib/pgsql/backup.tar /var/lib/pgsql/data/
    psql -c "select pg_stop_backup();"
    rm /var/lib/pgsql/backup_in_progress
    tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/

开关文件`/var/lib/pgsql/backup_in_progress`是第一次创建， 使已完成的WAL文件的归档发生。在备份后删除开关文件。归档的WAL文件然后添加至备份， 使两个基础备份和所有必需的WAL文件是相同的tar文件的一部分。 请记住，错误处理添加到您的备份脚本中。

### 24.3.6.2. 压缩归档日志

如果归档存储大小是一个问题，你可以使用gzip 压缩归档文件：

    archive_command = 'gzip < %p > /var/lib/pgsql/archive/%f'

在恢复过程中你需要使用gunzip：

    restore_command = 'gunzip < /mnt/server/archivedir/%f > %p'

### 24.3.6.3. `archive_command`脚本

许多人选择使用脚本定义他们的`archive_command`， 因此`postgresql.conf`记录看起来很简单：

    archive_command = 'local_backup_script.sh "%p" "%f"'

任何你想在归档过程中使用不只是独立命令时。 使用一个单独的脚本文件是可取的，这允许所有的复杂性在脚本中管理， 这可以使用流行的脚本语言书写，如bash或者perl。

可能在脚本中解决的所需例子包含：

- 拷贝数据到安全异地数据存储

- 计量WAL文件以致于他们每三个小时改变，而不是一个时间。

- 其他备份和恢复软件接口

- 监控软件报告错误接口

> 提示: 当使用`archive_command`脚本时，期望启动logging_collector。 来自脚本写入stderr中的任何消息将出现在数据库服务器日志中， 如果失败，则允许简单地诊断复杂配置。

## 24.3.7. 警告

目前，在线备份技术还有几个局限。它们可能在将来的版本中修补：

- 在Hash索引上的操作目前没有使用WAL记录日志，所以重放就不会更新这些索引类型。 这将意味着任何新的插入被索引忽略，已更新行显然会消失，并且已删除行将仍然保留指针。 换句话说，如果你修改了带有hash索引的表，那么你将获得备用服务器上不正确的查询结果。 当完成恢复时，建议是在完成恢复操作之后手工REINDEX每个这样的索引。

- 如果在进行数据库备份的时候发出一个CREATE DATABASE命令， 然后在这个过程中`CREATE DATABASE`命令拷贝的模板数据库被修改了， 那么用这个备份进行恢复的数据库很有可能导致这些修改也传播到新创建的数据库中去。 这个行为当然是不愿意看到的。为了避免这个风险， 最好在进行数据库备份的时候不要修改任何模板数据库。

- CREATE TABLESPACE命令是用文本的绝对路径记录WAL日志的， 因此会以相同的绝对路径重新创建。如果日志是在另外一台机器上重放， 那么这个行为可能不是我们想要的。即使在同一台机器， 但是在一个新的数据目录里重放日志，都很可能是危险的： 重放仍将会覆盖原来的表空间的内容。 为了避免这类的潜在问题， 最好的方法是在创建或者删除表空间之后进行一次新的基础备份。

还要注意，缺省的WAL格式体积相当大，因为它包含许多磁盘页快照。 这些磁盘页快照是设计来支持崩溃恢复的， 因为我们可能需要修补部分写入的磁盘页。根据你的系统硬件和软件的不同， 这种部分写入的危险可能是微乎其微的。这种情况下， 你可以通过使用full_page_writes关闭磁盘页面快照， 从而大大减少归档日志的总尺寸(在你这么做之前，阅读第 29 章里面的注意和警告)。 关闭页面快照并不阻止日志使用PITR操作。 一个将来需要开发的功能是在`full_page_writes`打开的时候， 通过删除不需要的磁盘页拷贝来压缩归档的 WAL 数据。同时， 管理员可以通过尽量合理地增加检查点的时间间隔来减少包含在WAL里的页面快照。

上一页 | 起始页 | 下一页
文件系统级别备份 | 上一级 | 高可用性与负载均衡，复制