热备

PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 25. 高可用性与负载均衡，复制 | 下一页

# 25.5. 热备

热备术语是用来形容连接到服务器，并运行只读查询的能力，而服务器在归档恢复或备模式。 对复制目的和非常精确的备份恢复到所需的状态，这是非常有用的。 长期的热备，也指从恢复到正常运行的服务器的能力， 而用户继续运行的查询和/或保持连接开放。

在热备用模式运行查询与正常的查询操作类似，虽然有几个使用和管理的差异解释如下。

## 25.5.1. 用户概述

当备用服务器上hot_standby参数的设置为真时，将开始接受连接， 一旦恢复带来的系统到一致的状态。所有这些连接都严格只读的， 甚至可能没有可写的临时表。

数据从主服务器到备服务器上需要一些时间，所以会有一个主备数据库间的可测量的延迟。 因此，在主备服务器上几乎同时运行同样的查询返回不同的结果。 我们说在备服务器上的数据最终与主服务器上的一致。 一旦事务提交记录在备服务器是上回放， 由事务产生的变化对于在备服务器上的任何新快照来说是可见的。 快照可能是在每个查询或事务的开始，取决于当前事务的隔离级别。 请参阅第 13.2 节获取更多的信息。

热备期间开始的事务可能会发出下面的命令：

- 查询访问-`SELECT`，`COPY TO`

- 游标命令-`DECLARE`，`FETCH`，`CLOSE`

- 参数-`SHOW`，`SET`，`RESET`

- 事务管理命令

  - `BEGIN`, `END`, `ABORT`, `START TRANSACTION`

  - `SAVEPOINT`, `RELEASE`, `ROLLBACK TO SAVEPOINT`

  - `EXCEPTION`阻塞其它内部的子事物。

- `LOCK TABLE`仅当明确这些模式之一: `ACCESS SHARE`, `ROW SHARE`或者`ROW EXCLUSIVE`。

- 规划和资源 - `PREPARE`, `EXECUTE`, `DEALLOCATE`, `DISCARD`

- 插件和扩展 - `LOAD`

在热备期间开始的事务，将从不会分配事务ID，并且不能写入到系统预写日志。 因此，以下操作将产生错误消息：

- 数据操纵语言(DML) - `INSERT`, `UPDATE`, `DELETE`, `COPY FROM`, `TRUNCATE`。 请注意，不允许操作在恢复期间正执行触发器的结果。 此限制也适用于临时表，因为不分配一个事务ID，不能读取或写入表行， 在一个热备环境这种情况是不可能的。

- 数据定义语言(DDL) - `CREATE`,`DROP`, `ALTER`, `COMMENT`。 甚至临时表也适用这个限制，因为执行这些操作将需要更新系统空间表。

- `SELECT ... FOR SHARE | UPDATE`因为行锁，不能不采取更新底层数据文件。

- 在`SELECT`语句上的规则产生DML命令。

- `LOCK`明确要求一个高于`ROW EXCLUSIVE MODE`的模式。

- `LOCK`简短的缺省形式，因为它请求`ACCESS EXCLUSIVE MODE`.

- 事务管理命令明确设置非只读状态：

  - `BEGIN READ WRITE`, `START TRANSACTION READ WRITE`

  - `SET TRANSACTION READ WRITE`, `SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE`

  - `SET transaction_read_only = off`

- 两阶段提交命令 - `PREPARE TRANSACTION`, `COMMIT PREPARED`, `ROLLBACK PREPARED` 因为即使只读事务需要在准备阶段写WAL。（两种阶段提交的第一个阶段）。

- 序列更新 - `nextval()`, `setval()`

- `LISTEN`, `UNLISTEN`, `NOTIFY`

在正常的操作，允许"只读"事务更新序列，使用`LISTEN`, `UNLISTEN`和`NOTIFY`， 所以热备会话下操作会比通常的只读会话限制稍微更严格。 在将来的版本中这些限制中的一些可能会放宽。

热备间，`transaction_read_only`这个参数总为真，可能不会变。 但只要没有试图修改数据库，在热备的连接，将行动就像任何其它的数据库连接。 如果发生失效切换或倒换,数据库将切换到正常的处理模式。 当服务器改变模式， 会话将保持连接。一旦热备完成，有可能初始化读写事务（即使从热备间的会话）。

通过发出的`SHOW transaction_read_only`将告诉用户他们的会话是否只读的。 另外，一组函数允许用户访问关于备服务器的信息（请参阅表 9-66） 这些允许你写程序获知数据库的当前状态。这些可以用来监视恢复进程， 或允许你写复杂的程序来恢复数据库到特定状态。

## 25.5.2. 处理查询冲突

主备服务器是许多方式松散连接的。在主服务器上的活动将在备服务器上生效。 作为一个结果，它们之间有潜在的负面交互或冲突.最容易理解的冲突是性能： 如果在主服务器上发生大数据量加载，然后将在备服务器上产生类似的WAL记录流， 所以备服务器查询可能竞争系统资源，像I/O。

在热备也可能发生额外的类型冲突。在该场景下，这些冲突是硬冲突。 可能需要取消查询，在某些情况下，为了解决它们，断开连接。 给用户提供几种解决这些冲突的方法。冲突情况包括：

- 在主服务器上采取访问排斥锁，包括明确的`LOCK`命令和多种DDL操作，在备服务器查询访问表冲突。

- 在主服务器上删除表空间与备服务器查询使用该空间的临时工作文件冲突。

- 在主服务器上删除一个数据库与在备服务器上连接到那个数据库的会话冲突。

- 一个从WAL清空记录的应用程序vacuum与在备服务器上事务，其快照仍然可以"看到"已删除的行。

- 一个从WAL清空记录的应用程序vacuum与在备服务器上查询访问该目标页，不管要删除的数据是否可见。

在主服务器上，这些情况简单等待结果，用户可能选择取消任何冲突的操作。 尽管，在备服务器上没有选择： 在主服务器上已经发生的WAL日志， 所以备服务器应用它一定不会失败。此外，允许WAL应用无限期等待可能是很不明智的。 因为备服务器的状态将变为增量远落后主服务器的。因此，提供一个机制， 强行取消备服务器上与将要应用WAL记录冲突的查询。

一个该问题情况的例子是管理员在主服务器上运行`DROP TABLE`一张表，而备服务器当前正查询这张表。 如果在备服务器上执行了`DROP TABLE`，明确的备服务器查询不能继续。 如果这个问题情况发生在主服务器。则`DROP TABLE`将等到其它查询完成。 但是当`DROP TABLE`运行在主服务器时，主服务器不会有关于备服务器查询的信息， 因此，将不等待任何备服务器查询。当备服务器查询在运行时，WAL改变的记录来到备服务器， 导致一个冲突。备服务器要么延迟应用WAL记录（任何事情也都要在它们之后）， 不然取消冲突的查询，由此可以应用`DROP TABLE`。

当一个冲突查询短的，通常想要允许它完成而延迟WAL应用程序一点点。 但是长时间的延迟WAL应用程序通常不是想要的。 所以取消机制有参数max_standby_archive_delay和max_standby_streaming_delay， 这定义在WAL应用程序中允许延迟最大值。一旦查询冲突比应用任何新收取的WAL数据设定有关延迟长， 则取消查询冲突。 有两个参数，因此有两个不同延迟，为从归档读取WAL数据 （即从一个基准备份初始化恢复或"赶上"已经远落后的备服务器） 和通过流复制读取WAL数据的指定延迟。

在备服务器存在高可用性的主服务器，最好设置延迟参数相对短， 因此不会由备服务器查询所导致延迟使远落后主服务器。 不过，如果备服务器意思为执行长时间的查询，那么一个高的或无期限的延迟值是可取的。 请记着如果延迟WAL记录应用程序，则长时间查询将导致 备服务器上的其它会话不能看到最新的变化。

一旦超过了由`max_standby_archive_delay`或者 `max_standby_streaming_delay`指定的延迟， 将取消查询冲突。 这通常结果是一个取消错误，虽然在回放`DROP DATABASE`整个数据库的情况下， 将终止冲突会话。此外，如果冲突由空闲事务保持，终止冲突会话。 （这个行为可能在将来版本改变）。

可能立即重试已取消的查询（在开始一个新事务之后，当然）。 自查询取消依赖于WAL记录重播的本质，如果再次执行，已经取消的查询可能很成功。

请记住这些参数与从备服务器接收WAL数据开始所经过的时间比较。 允许备服务器上任何查询的宽期限，从不超过该延迟参数， 并且如果备服务器存在落后主服务器，那么期限的可能相当小。 如等待之前查询执行完成的结果，或不能跟上有大量的更新负载的结果。

备用的查询和WAL重放之间的冲突最常见的原因是"过早清除"。 通常，PostgreSQL允许清理老行版本，当没有事务需要根据MVCC的规则 看到他们保证数据的正确性。 然而，这条规则只适用于在主库上事务执行情况。 所以在主库上清理删除主库上事务仍然可见的行版本，这是可能的。

有经验的用户应注意行版本的清理和行版本冷冻将与备用查询冲突。手动运行 `VACUUM FREEZE`可能引起甚至没有更新或删除行的表上的冲突。

用户应该清楚那些表， 在主服务器上定期和大量更新表将会很快导致取消备服务器上长时间运行的查询。 在这类情况下，对`max_standby_archive_delay`或者 `max_standby_streaming_delay`设置一个有限值, 类似于设置`statement_timeout`。

如果发现不能接受某些取消备服务器查询，补救存在的可能性。 第一个选项是设置参数`hot_standby_feedback`， 阻止`VACUUM`删除最近的死行， 所以清理冲突不会发生。如果你这样做，你应该知道这将延迟主服务器清理死行，其可能不想要的表膨胀结果。 不过这种情况清理不逊于如果备服务器查询直接运行在主服务器上， 并且你仍然得到卸载执行在备服务器上的好处。 如果备用服务器频繁的连接和断开连接，你可能想要做出调整， 以处理没有提供`hot_standby_feedback`反馈的时期。 例如，考虑增加`max_standby_archive_delay`， 这样在未连接的时期内，查询不会因为WAL归档文件中的冲突而迅速取消。 也应该考虑增加`max_standby_streaming_delay`， 以避免被重新连接之后新到达的流WAL条目迅速取消。

另一个选项是在主服务器上增加vacuum_defer_cleanup_age， 从而将不会像通常很快的清理掉死行。 这将允许在备服务器上取消它们前，更多时间给执行查询， 无需设置一个高的`max_standby_streaming_delay`。 虽然用这种方法保证窗口的执行时间是有困难的， 因为`vacuum_defer_cleanup_age`在主服务器执行的事务中是可测的。

查询数取消，原因可以看作在备用服务器上使用`pg_stat_database_conflicts`系统视图。 `pg_stat_database`系统视图也包含摘要信息。

## 25.5.3. 管理员概述

如果在`postgresql.conf`中`启用`了`hot_standby`， 并且目前有个`recovery.conf`文件， 该服务器将运行在热备模式。不过可能花些时间为允许的热备连接， 因为该服务器不接受连接直到完成足够的恢复能提供一致的状态，其查询能运行。 在这个期间，将带有一个错误消息拒绝客户端尝试连接。为确认该服务器起来了， 要么循环尝试从应用程序连接，或者在服务器日志里查看这些错误消息：

    LOG:  entering standby mode

    ... then some time later ...

    LOG:  consistent recovery state reached
    LOG:  database system is ready to accept read only connections

在主服务器上每个检查点都记录一次一致信息。 在主服务器上没有将`wal_level`设置为`hot_standby` 或`logical`时，当读取正在写的WAL时， 启用热备是不可能的。存在这些条件的两者也可能延迟达到一致性状态：

- 一个写事务有多于64个子事务

- 很长时间活动的写事务

如果你正运行基于文件日志传送（“暖备”）， 你可能需要等到下一个WAL文件到来，其尽可能长如在主服务器设置`archive_timeout`。

有些参数的设置在备服务器将需要重新配置，如果在主服务器改变了它们。 对于这些参数，备服务器上的值要大于或等于主服务器上的。 如果这些参数没有设置足够高，那么备服务器将拒绝启动。 提供了更高的值，重启该服务器再开始恢复。这些参数是：

- `max_connections`

- `max_prepared_transactions`

- `max_locks_per_transaction`

管理员选择合适的设置为max_standby_archive_delay和 max_standby_streaming_delay是很重要的。 根据业务的优先级，最好的选择有所不同。例如：如果服务器是主要任务， 作为高可用性的服务器，那么你想低延迟设置，也许设置为0，尽管这也是很积极的设置。 如果备服务器的任务作为决策支持的额外服务器，那么可能接受设置最大延迟为几个小时， 或甚至-1意味着永远等待查询完成。

在主服务器上写的事务状态"提示位"没有记录WAL日志，所以在备服务器上将或许再次重写该提示。 因此，备服务器将仍然进行写磁盘即使所有用户是只读的，数据值自身没有发生改变。 用户将仍然写大量排序的临时文件和 重新生成缓存的信息文件， 所以在热备模式数据库没有部分是真只读的。还要注意写到远程数据库使用dblink模块， 外部数据的操作使用PL函数仍然是可能的，尽管事务是本地只读的。

在恢复模式里，不接受下面类型的管理命令：

- 数据定义语言(DDL) - e.g. `CREATE INDEX`

- 权限和所有权 - `GRANT`, `REVOKE`,`REASSIGN`

- 维护命令 - `ANALYZE`, `VACUUM`,`CLUSTER`, `REINDEX`

再次，请注意在主服务器的“只读”模式事务中，允许这里的某些命令。

作为一个总结，你不能创建额外的索引，统计也不能仅在备服务器， 如果需要这些管理命令，应该在主服务器执行，并且最终这些变化将传播到备服务器。

`pg_cancel_backend()`和`pg_terminate_backend()` 将在用户后台工作，但是不启动进程，其执行恢复。 `pg_stat_activity`将不显示为一个启动进程项，也不显示做恢复事务的活动。 作为一个总结，`pg_prepared_xacts`在恢复中总是空。 如果你愿解决有疑问准备的事务， 在主服务器上查看`pg_prepared_xacts`和发出命令来解决这里的事务。

`pg_locks`将显示由后台持有的锁。 `pg_locks`也显示由启动进程所管理的虚拟事务， 其拥有由恢复正回放的事务所持有的`AccessExclusiveLocks`。 请注意该启动进程不需要锁定数据库变化，并且非`AccessExclusiveLocks`锁， 不会显示在启动进程的`pg_locks`里。它们只是推测存在。

Nagios插件check_pgsql将工作，因为用它检测存在的简单信息。 check_postgres监控脚本也将工作， 尽管有些报告值能给不同或迷惑的结果。 例如：上次清理时间将不会保持， 因为在备服务器没有清理发生。 运行在主服务器的清理，将仍然发送它们的改变到备服务器。

在恢复期间WAL文件控制命令将不工作，比如`pg_start_backup`, `pg_switch_xlog`等。

动态加载模块工作，包括`pg_stat_statements`。

在恢复中咨询锁将工作正常，包括死锁保护。 请注意咨询锁从不写WAL日志，所以对于一个咨询锁在主服务器上或回放WAL在备服务器上冲突不可能的。 在主服务器上需要一个咨询锁，在备服务器上已经初始化了一个类似咨询锁也是不可能的。 咨询锁只是与需要它们的服务器相关。

基于触发器的复制系统像Slony, Londiste和Bucardo将不在备服务器运行， 尽管在主服务器运行的很好，但变化不会发送到备服务器应用。WAL回放不是基于触发器的， 所以你不能从备服务器中传送到任何系统，其需要额外的写或依赖使用触发器。

不能分配新OID，尽管某些UUID生成器可能仍然工作，只要不依靠它们写新状态到数据库。

当前，在只读事务中不允许创建临时表，所以在某些情况下存在的脚本将运行不正确。 这个限制可能在以后的版本中放宽。这是一个SQL标准的兼容性和技术问题。

如果表空间是空，`DROP TABLESPACE`只能成功。 有些备服务器用户可积极的通过`temp_tablespaces`参数使用 该表空间。如果在表空间有临时文件，取消所有活动的查询来确保删除临时文件，所以可以删除表空间， 可以继续WAL回放。

在主服务器上运行`DROP DATABASE`或者`ALTER DATABASE ... SET TABLESPACE`将产生一个WAL项， 其将导致已连接到在备服务器上的那个数据库的所有用户，强制断开连接。 这个动作立即发生， 不管`max_standby_streaming_delay`的设置。请注意`ALTER DATABASE ... RENAME`不会断开连接的用户， 在多数情况下忽视，不过如果某些方式依赖数据库名，可能在某些情况下导致一个程序混乱。

在正常（非恢复）模式，如果你发出`DROP USER`或者`DROP ROLE`对于一个有登录权限的角色， 当那个用户仍然已经连接，那么不会发生什么对于已连接的用户-他们保持连接。 不过该用户不能再连接。这个行为在恢复中也适用，所以在主服务器上`DROP USER`不能断开备服务器上该用户连接。

在恢复中统计采集器是活动的。所有扫描，读取，块，索引使用等，将在备服务器中记录。 回放活动将不复制在主服务器上的影响，因此回放插入将不增加插入pg_stat_user_tables列。 恢复开始删除该统计文件， 所以主备服务器的统计将不同，认为这是个特性，而不是一个Bug。

在恢复中自动清理是不活跃的。在恢复结束将正常启动。

在恢复中后台记录器是活动的，将执行重启点（类似于主服务器上的检查点）和正常块清理活动。 这可能包含存储在备服务器上的命中位更新。 在恢复中接受`CHECKPOINT`命令，尽管执行一个重启点而不是一个新检查点。

## 25.5.4. 热备参数参考

各种参数已经在第 25.5.2 节和 第 25.5.3 节上面提到。

在主服务器上，可以使用参数wal_level和 vacuum_defer_cleanup_age。如果在主服务器上设置， max_standby_archive_delay和 max_standby_streaming_delay没有影响。

在备服务器,可以使用参数hot_standby, max_standby_archive_delay和 max_standby_streaming_delay。 只要服务器保留在备模式，vacuum_defer_cleanup_age没有影响， 尽管变为相关的，如果备服务器成为主服务器。

## 25.5.5. Caveats

有几个热备限制。这些可能在将来的版本中解决：

- 在哈希索引的操作，不会记录在目前的WAL日志，索引回放将不更新这些索引。

- 在做快照之前充分认识运行的事务是必需的。 事务使用大量的子事务（当前大于64）将延迟只读连接的开始直到运行最长写事务完成。 如果这种情况发生，说明消息将发送到服务器的日志。

- 对于备服务器查询的有效开始点是产生在主服务器上的每个检查点。 如果备服务器关机，当主服务器在关机状态， 不可能重进热备直到启动主服务器， 所以在WAL日志里产生进一步的开始点。在最常见的情况下这种情况不是一个问题， 它可能发生。一般地，如果主服务器关机，不再可用，那可能由于一个严重的失败， 需要将备服务器转化为新主服务器运行。 并且有特意取下主服务器的情况， 协调确保备服务器成为平滑的主服务器，也是标准的处理。

- 在恢复结束，由准备的事务持有的`AccessExclusiveLocks`需要锁表正常数量的条目的两倍。 如果你计划运行大量并发的准备事务，正常地用`AccessExclusiveLocks`或你计划一个 大事务用多个`AccessExclusiveLocks`， 建议你选择一个大的`max_locks_per_transaction`值, 可能为在主服务器上两倍这个参数值。如果你设置`max_prepared_transactions`为0， 根本不需要考虑这个。

- 串行化事务隔离级别在热备份中仍然不可用（参阅第 13.2.3 节 and 第 13.4.1 节获取更多信息）。 在热备份模式中尝试设置事务为串行化隔离级别将产生一个错误。

上一页 | 起始页 | 下一页
日志传送的替代方法 | 上一级 | 恢复配置