热备(hot standby)是指在服务器处于归档恢复或备用模式时,能够连接到服务器并运行只读查询的能力。这对于复制以及精确地将备份恢复到所需状态都非常有用。“热备”这个术语也指服务器能够从恢复模式过渡到正常运行模式,而用户可以继续运行查询和/或保持连接打开。
在热备模式下运行查询与正常查询操作类似,但存在一些使用和管理上的差异,下文将对此进行解释。
当备用服务器上的 hot_standby 参数设置为 true 时,一旦恢复将系统带入一致状态,备用服务器就会开始接受连接并为热备做好准备。所有此类连接都严格为只读;即使是临时表也不能写入。
备用服务器上的数据需要一些时间才能从主服务器到达,因此主服务器和备用服务器之间会存在可衡量的延迟。因此,在主服务器和备用服务器上几乎同时运行相同的查询可能会返回不同的结果。我们称备用服务器上的数据与主服务器是最终一致的。一旦主服务器上某个事务的提交记录在备用服务器上重放,该事务所做的更改对在备用服务器上获取的任何新快照都可见。快照可能在每个查询开始时或每个事务开始时获取,具体取决于当前的事务隔离级别。有关更多详细信息,请参见第 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
UNLISTEN
在热备期间开始的事务永远不会被分配事务 ID,也不会写入系统预写日志 (WAL)。因此,以下操作会产生错误消息:
数据操作语言 (DML):INSERT, UPDATE, DELETE, MERGE, COPY FROM, TRUNCATE。请注意,不允许执行会导致触发器在恢复期间被执行的操作。此限制甚至适用于临时表,因为在没有分配事务 ID 的情况下无法读取或写入表行,而这在当前热备环境中是不可能的。
数据定义语言 (DDL):CREATE, DROP, ALTER, COMMENT。此限制甚至适用于临时表,因为执行这些操作需要更新系统目录表。
SELECT ... FOR SHARE | UPDATE,因为在不更新底层数据文件的情况下无法获取行锁。
生成 DML 命令的 SELECT 语句上的规则。
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, NOTIFY
在正常操作中,“只读”事务允许使用 LISTEN 和 NOTIFY,因此热备会话的操作比普通只读会话受到更严格的限制。未来版本可能会放宽其中一些限制。
在热备期间,transaction_read_only 参数始终为 true,且无法更改。但是,只要不尝试修改数据库,热备期间的连接将与任何其他数据库连接一样工作。如果发生故障转移或切换,数据库将切换到正常处理模式。会话在服务器更改模式期间将保持连接。一旦热备完成,就可以发起读写事务(即使是从热备期间开始的会话)。
用户可以通过发出 SHOW in_hot_standby 来确定其会话当前是否处于热备状态。(在 14 版本之前的服务器版本中,in_hot_standby 参数不存在;对于旧服务器,一个可行的替代方法是 SHOW transaction_read_only。)此外,一组函数(表 9.98)允许用户访问有关备用服务器的信息。这些允许您编写了解数据库当前状态的程序。这些程序可用于监视恢复进度,或允许您编写将数据库恢复到特定状态的复杂程序。
主服务器和备用服务器在很多方面是松散连接的。主服务器上的操作会影响备用服务器。因此,它们之间存在负面交互或冲突的可能性。最容易理解的冲突是性能:如果主服务器正在进行大量数据加载,那么它将在备用服务器上生成类似的 WAL 记录流,因此备用查询可能会争用系统资源,例如 I/O。
此外,热备还可能发生其他类型的冲突。这些冲突是硬冲突,意味着查询可能需要被取消,在某些情况下,会话也可能被断开以解决它们。用户可以通过几种方式来处理这些冲突。冲突的案例包括:
在主服务器上获取的访问排他锁,包括显式的 LOCK 命令和各种DDL操作,与备用查询中的表访问发生冲突。
在主服务器上删除表空间会与在备用服务器上使用该表空间进行临时工作文件的备用查询发生冲突。
在主服务器上删除数据库会与连接到备用服务器上该数据库的会话发生冲突。
从 WAL 应用的清理记录与仍然可以“看到”要删除的任何行的备用事务发生冲突。
从 WAL 应用的清理记录与访问备用服务器上目标页面的查询发生冲突,无论要删除的数据是否可见。
在主服务器上,这些情况只会导致等待;用户可以选择取消其中一个冲突的操作。然而,在备用服务器上没有选择: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 规则正确地可见数据。然而,这个规则只能应用于主服务器上执行的事务。因此,主服务器上的清理可能会删除备用服务器上事务仍然可见的行版本。
行版本清理不是与备用查询冲突的唯一潜在原因。所有仅索引扫描(包括在备用服务器上运行的扫描)都必须使用一个MVCC与可见性图(visibility map)“一致”的快照。因此,当 VACUUM 将页面设置为所有可见(all-visible)在可见性图中 并且该页面包含一个或多个不对所有备用查询可见的行时,就会发生冲突。因此,即使对没有更新或删除的行进行 VACUUM 也可能导致冲突。
用户应该清楚,在主服务器上定期大量更新的表将很快导致备用服务器上长期运行的查询被取消。在这种情况下,为 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 条目快速取消。
可以通过查询备用服务器上的 pg_stat_database_conflicts 系统视图来查看查询取消的数量及其原因。pg_stat_database 系统视图也包含摘要信息。
用户可以通过 log_recovery_conflict_waits 参数控制 WAL 重放等待冲突的时间超过 deadlock_timeout 时是否生成日志消息。
如果在 postgresql.conf 中设置 hot_standby 为 on(默认值)并且存在 standby.signal 文件,则服务器将以热备模式运行。但是,热备连接可能需要一些时间才能允许,因为服务器在完成足够的恢复以提供一致的状态以供查询运行时不会接受连接。在此期间,尝试连接的客户端将收到错误消息被拒绝。要确认服务器已启动,请从应用程序尝试循环连接,或查看服务器日志中的这些消息:
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 未设置为 replica 或 logical 时生成的 WAL 时,无法启用热备。即使达到一致状态,如果满足以下两个条件,恢复快照可能仍未准备好进行热备,从而延迟接受只读连接。要启用热备,主服务器上需要关闭具有超过 64 个子事务的长生存期写事务。
一个写事务有超过 64 个子事务
非常长生存期的写事务
如果您正在使用基于文件的日志传输(“温备”),您可能需要等到下一个 WAL 文件到达,这可能长达主服务器上的 archive_timeout 设置。
一些参数的设置决定了用于跟踪事务 ID、锁和已准备事务的共享内存大小。为了确保备用服务器在恢复期间不会耗尽共享内存,这些共享内存结构在备用服务器上不应小于主服务器上。例如,如果主服务器使用了已准备事务,但备用服务器没有为跟踪已准备事务分配任何共享内存,那么在更改备用服务器的配置之前,恢复将无法继续。受影响的参数是:
max_connections
max_prepared_transactions
max_locks_per_transaction
max_wal_senders
max_worker_processes
要确保这不会成为问题,最简单的方法是在备用服务器上将这些参数设置为等于或大于主服务器上的值。因此,如果您想增加这些值,应该先在所有备用服务器上进行,然后再将更改应用于主服务器。反之,如果您想减小这些值,应该先在主服务器上进行,然后再将更改应用于所有备用服务器。请记住,当备用服务器被提升为主服务器时,它将成为其下游备用服务器的所需参数设置的新参考。因此,为了避免在切换或故障转移期间出现此问题,建议在所有备用服务器上保持这些设置相同。
WAL 跟踪主服务器上的这些参数的更改。如果热备处理的 WAL 表明主服务器上的当前值高于其自身的值,它将记录一个警告并暂停恢复,例如:
WARNING: hot standby is not possible because of insufficient parameter settings DETAIL: max_connections = 80 is a lower setting than on the primary server, where its value was 100. LOG: recovery has paused DETAIL: If recovery is unpaused, the server will shut down. HINT: You can then restart the server after making the necessary configuration changes.
此时,需要在备用服务器上更新设置并重新启动实例,然后才能继续恢复。如果备用服务器不是热备,那么当它遇到不兼容的参数更改时,它将立即关闭而不会暂停,因为保持其运行没有意义。
管理员选择适合 max_standby_archive_delay 和 max_standby_streaming_delay 的设置非常重要。最佳选择取决于业务优先级。例如,如果服务器主要任务是作为高可用性服务器,那么您将需要较低的延迟设置,甚至可能为零,尽管这是一个非常激进的设置。如果备用服务器的任务是作为决策支持查询的附加服务器,那么可以将最大延迟值设置为数小时,甚至 -1,表示永远等待查询完成。
在主服务器上写入的事务状态“提示位”(hint bits)不进行 WAL 日志记录,因此备用服务器上的数据很可能会在备用服务器上重新写入提示位。因此,即使所有用户都是只读的,备用服务器仍然会执行磁盘写入;数据值本身没有变化。用户仍然会写入大的排序临时文件并重新生成 relcache 信息文件,因此在热备模式下,数据库的任何部分都不是真正只读的。另外请注意,使用 dblink 模块写入远程数据库以及使用 PL 函数进行数据库外的其他操作仍然是可能的,即使事务在本地是只读的。
以下类型的管理命令在恢复模式下不被接受:
数据定义语言 (DDL):例如,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 监控脚本也可以工作,尽管一些报告的值可能会给出不同的或令人困惑的结果。例如,上次 vacuum 时间将不会被维护,因为备用服务器上没有 vacuum。在主服务器上运行的 vacuum 确实会将其更改发送到备用服务器。
WAL 文件控制命令在恢复期间将不起作用,例如 pg_backup_start, pg_switch_wal 等。
动态加载的模块可以工作,包括 pg_stat_statements。
广告锁(Advisory locks)在恢复期间正常工作,包括死锁检测。请注意,广告锁永远不会进行 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 不会断开备用服务器上该用户的连接。
累积统计系统在恢复期间是活动的。所有扫描、读取、阻塞、索引使用等都会在备用服务器上正常记录。但是,WAL 重放不会增加关系和数据库特定的计数器。即,重放不会增加 pg_stat_all_tables 的列(例如 n_tup_ins),也不会在 pg_statio_ 视图中跟踪启动进程执行的读写操作,也不会增加相关的 pg_stat_database 列。
自动清理(Autovacuum)在恢复期间不活跃。它将在恢复结束时正常启动。
检查点进程(checkpointer process)和后台写入进程(background writer process)在恢复期间是活动的。检查点进程将执行重新启动点(restartpoints)(类似于主服务器上的检查点),后台写入进程将执行正常的块清理活动。这可以包括更新存储在备用服务器上的提示位信息。CHECKPOINT 命令在恢复期间被接受,尽管它执行的是重新启动点而不是新的检查点。
前面在第 26.4.2 节和第 26.4.3 节中提到了各种参数。
在主服务器上,可以使用 wal_level 参数。max_standby_archive_delay 和 max_standby_streaming_delay 在主服务器上设置无效。
在备用服务器上,可以使用 hot_standby, max_standby_archive_delay 和 max_standby_streaming_delay 参数。
热备存在一些限制。这些限制可能会也在未来的版本中得到修复,而且很可能会被修复。
在获取快照之前,需要对正在运行的事务有完整的了解。使用大量子事务(当前超过 64 个)的事务将延迟只读连接的启动,直到最长的写事务完成为止。如果发生这种情况,将向服务器日志发送说明性消息。
备用查询的有效起始点在主服务器的每个检查点生成。如果在主服务器处于关闭状态时备用服务器关闭,则可能无法重新进入热备,直到主服务器启动,以便它在 WAL 日志中生成更多的起始点。这种情况在可能发生的最常见情况下不是问题。通常,如果主服务器关闭且不再可用,那很可能是由于严重故障,需要备用服务器被转换为作为新主服务器运行。并且在主服务器被有意关闭的情况下,协调确保备用服务器顺利成为新主服务器也是标准程序。
在恢复结束时,由已准备事务持有的 AccessExclusiveLocks 将需要正常数量的两倍的锁表条目。如果您计划运行大量通常会获取 AccessExclusiveLocks 的并发已准备事务,或者您计划有一个获取大量 AccessExclusiveLocks 的大型事务,建议您为 max_locks_per_transaction 选择一个更大的值,可能达到主服务器上参数值的两倍。如果您将 max_prepared_transactions 设置为 0,则完全不需要考虑此问题。
串行化事务隔离级别(Serializable transaction isolation level)在热备中尚不可用。(有关详细信息,请参见第 13.2.3 节和第 13.4.1 节。)在热备模式下尝试将事务设置为串行化隔离级别将生成一个错误。