选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.0 已于 2015 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

25.5. 热备 #

热备是用来描述服务器在归档恢复或备库模式下仍然可以接受连接并运行只读查询的能力的术语。这对于复制用途以及把备份以极高精度恢复到目标状态都很有用。术语热备还指服务器能够在用户持续运行查询和/或保持连接不断开的同时,从恢复状态切换到正常运行状态的能力。

在热备模式下运行查询与正常查询操作类似,不过正如下文所述,在使用和管理上存在一些差异。

25.5.1. 用户概览 #

当备库上的hot_standby参数被设置为真时,一旦恢复把系统带到一致状态,它就会开始接受连接。所有这类连接都严格是只读的,甚至不能写入临时表。

备库上的数据需要一些时间才能从主库到达,因此主库和备库之间会有可测量的延迟。因此,在主库和备库上几乎同时运行同一查询,可能会返回不同的结果。我们说备库上的数据与主库是最终一致的。一旦某个事务的提交记录在备库上被重放,该事务所做的修改就会对备库上之后取得的所有新快照可见。快照可以在每个查询开始时取得,也可以在每个事务开始时取得,这取决于当前的事务隔离级别。详见第 13.2 节

热备期间启动的事务可以发出以下命令:

  • 查询访问 —— SELECTCOPY TO

  • 游标命令 —— DECLAREFETCHCLOSE

  • 参数 —— SHOWSETRESET

  • 事务管理命令

    • BEGIN, END, ABORT, START TRANSACTION

    • SAVEPOINTRELEASEROLLBACK TO SAVEPOINT

    • EXCEPTION块及其他内部子事务

  • LOCK TABLE,但仅当显式处于以下模式之一:ACCESS SHAREROW SHAREROW EXCLUSIVE

  • 计划和资源 —— PREPAREEXECUTEDEALLOCATEDISCARD

  • 插件和扩展 —— LOAD

热备期间启动的事务永远不会被分配事务 ID,也不能写系统预写式日志。因此,以下操作将产生错误消息:

  • Data Manipulation Language (DML) - INSERT, UPDATE, DELETE, COPY FROM, TRUNCATE. 注意,没有任何被允许的操作会导致触发器在恢复期间执行。这一限制甚至适用于临时表,因为不分配事务 ID 就无法读写表行,而热备环境目前无法分配事务 ID。

  • Data Definition Language (DDL) - CREATE, DROP, ALTER, COMMENT. 这一限制甚至适用于临时表,因为执行这些操作需要更新系统目录表。

  • SELECT ... FOR SHARE | UPDATE,因为不更新底层数据文件就无法取得行锁。

  • SELECT 语句上生成 DML 命令的规则。

  • 显式请求高于 ROW EXCLUSIVE MODE 模式的 LOCK

  • 短默认形式的 LOCK,因为它请求的是 ACCESS EXCLUSIVE MODE

  • 显式设置非只读状态的事务管理命令:

    • BEGIN READ WRITE, START TRANSACTION READ WRITE

    • SET TRANSACTION READ WRITESET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE

    • SET transaction_read_only = off

  • 两阶段提交命令 —— PREPARE TRANSACTIONCOMMIT PREPAREDROLLBACK PREPARED,因为即使是只读事务,在准备阶段(两阶段提交的第一阶段)也需要写 WAL。

  • 序列更新 —— nextval()setval()

  • LISTENUNLISTENNOTIFY

在正常运行中,只读事务允许更新序列,也允许使用LISTENUNLISTENNOTIFY,因此热备会话受到的限制比普通只读会话略严。其中某些限制有可能在未来的版本中放宽。

在热备期间,参数transaction_read_only始终为真,并且不能更改。不过,只要不尝试修改数据库,热备期间的连接行为与其他数据库连接大致相同。如果发生故障切换或计划内切换,数据库将切换到正常处理模式。服务器切换模式时,会话仍会保持连接。一旦热备结束,就可以发起读写事务(即使该会话是在热备期间开始的)。

用户可以执行 SHOW transaction_read_only,判断自己的会话是否只读。此外,还有一组函数(表 9.58)可用于获取备库信息。借助它们,可以编写感知数据库当前状态的程序,用来监控恢复进度,或编写将数据库恢复到特定状态的复杂程序。

25.5.2. 处理查询冲突 #

主库和备库在许多方面都是松耦合的。主库上的动作会对备库产生影响,因此它们之间可能出现负面交互或冲突。最容易理解的冲突是性能:如果主库上正在执行一次大规模数据装载,那么备库上也会产生类似的 WAL 记录流,因此备库查询可能会争用系统资源,例如 I/O。

热备还可能发生其他几类冲突。这些冲突是硬冲突,意思是查询可能需要被取消,在某些情况下还可能需要断开会话来解决。系统为用户提供了多种处理这些冲突的方式。冲突情形包括:

  • 主库上取得的 ACCESS EXCLUSIVE 锁(包括显式的LOCK命令和各种DDL操作)与备库查询中的表访问冲突。

  • 主库上删除表空间与备库中使用该表空间存放临时工作文件的查询冲突。

  • 主库上删除数据库与备库上连接到该数据库的会话冲突。

  • 应用来自 WAL 的清理(vacuum)清理记录与快照仍能看到任何将被移除的行的备库事务冲突。

  • 应用来自 WAL 的清理清理记录与备库上访问目标页的查询冲突,无论要移除的数据是否可见。

在主库上,这些情况只会导致等待;用户可以选择取消冲突动作中的任意一方。但是在备库上没有这种选择:已经被写入 WAL 的动作早已在主库上发生,因此备库在应用它时不能失败。此外,让 WAL 应用无限期等待通常是很不可取的,因为备库的状态会越来越落后于主库。因此,系统提供了一种机制,用来强制取消那些与即将应用的 WAL 记录发生冲突的备库查询。

一个典型例子是,主库上的管理员在某个表上执行DROP TABLE,而备库上正有查询访问该表。显然,如果DROP TABLE在备库上被应用,该备库查询就无法继续。如果这种情况发生在主库上,DROP TABLE会等待直到其他查询结束。但在主库上执行DROP TABLE时,主库并不知道备库上正在运行哪些查询,因此它不会等待这些备库查询。于是,WAL 变更记录会在备库查询仍在运行时到达备库,从而引发冲突。备库要么延迟应用这条 WAL 记录(以及之后的所有记录),要么取消冲突查询,以便应用DROP TABLE

当冲突查询很短时,通常最好通过稍微延迟 WAL 应用来让它完成;但 WAL 应用长时间延迟通常并不可取。因此,取消机制提供了max_standby_archive_delaymax_standby_streaming_delay这两个参数,用于定义 WAL 应用允许的最大延迟。一旦应用新收到的 WAL 数据所花费的时间超过相应延迟设置,冲突查询就会被取消。之所以有两个参数,是为了分别针对从归档读取 WAL 数据的场景(即从基础备份进行初始恢复,或者追赶一台已经远远落后的备库)以及通过流复制读取 WAL 数据的场景指定不同的延迟值。

如果一台备库主要用于高可用性,那么最好把这些延迟参数设置得较短,这样服务器就不会因为备库查询导致的延迟而远远落后于主库。但是,如果该备库的用途是执行长时间运行的查询,那么较高甚至无限的延迟值可能更合适。不过要记住,如果某个长时间运行的查询延迟了 WAL 记录的应用,它也可能使备库上的其他会话看不到主库上的最新变更。

备库查询与 WAL 重放发生冲突的最常见原因是过早清理。正常情况下,当不再有事务需要看到旧行版本来保证符合 MVCC 规则的数据可见性时,PostgreSQL允许清理这些旧行版本。不过,这条规则只能应用于在主库上执行的事务。因此,主库上的清理有可能移除某个备库事务仍然可见的行版本。

有经验的用户应注意,行版本清理和行版本冻结都有可能与备库查询冲突。手动运行 VACUUM FREEZE 很可能导致冲突,即使表中没有更新过或删除过的行也是如此。

一旦超过max_standby_archive_delaymax_standby_streaming_delay指定的延迟,冲突的查询将被取消。这通常只是产生一个取消错误,但在重放DROP DATABASE的情况下,整个冲突会话都会被终止。此外,如果冲突涉及空闲事务所持有的锁,冲突会话也会被终止(这一行为将来可能改变)。

被取消的查询可以立即重试(当然要先开始一个新事务)。由于查询取消取决于正在重放的 WAL 记录的性质,被取消的查询再次执行时很可能成功。

请记住,延迟参数要与备库收到 WAL 数据之后经过的时间进行比较。因此,留给备库上任何一个查询的宽限期从不会超过延迟参数,并且如果备库已经由于等待之前的查询完成而落后或者因为过重的更新负载而无法跟上主库,宽限期可能会更少。

用户应当清楚,主库上经常并且大量更新的表,会很快导致备库上的长时间运行查询被取消。在这种情况下,把max_standby_archive_delaymax_standby_streaming_delay设置为有限值,可以视作类似于设置statement_timeout

如果发现备库查询取消的次数不可接受,有一些补救办法。第一种办法是连接到主库,并在需要于备库上运行查询的整个期间保持一个活动查询。这能防止VACUUM移除最近死亡的行,从而不会发生清理冲突。这可以用contrib/dblinkpg_sleep()或通过其他机制来实现。如果这样做,应注意这会延迟主库上死行的清理,可能导致不希望的表膨胀。不过,清理情况不会比备库查询直接运行在主库上更糟,而且你仍然获得把执行负载卸载到备库的好处。这种情况下必须把max_standby_archive_delay保持为较大的值,因为延迟的 WAL 文件中可能已包含与所需备库查询冲突的条目。

另一种办法是增大主库上的vacuum_defer_cleanup_age,使死行不会像通常那样很快被清理。这样查询在备库上被取消之前就有更多时间执行,而无须设置很高的max_standby_streaming_delay。不过,这种方法难以保证任何特定的执行时间窗口,因为vacuum_defer_cleanup_age是以主库上执行的事务数来计量的。

25.5.3. 管理员概览 #

如果postgresql.conf中把hot_standby设为on并且存在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期间写入的 WAL 时,无法启用热备。以下两种情况同时存在时,达到一致状态也可能被延迟:

  • 一个写事务包含超过 64 个子事务

  • 存活时间非常长的写事务

如果运行的是基于文件的日志传送(温备),可能需要等待下一个 WAL 文件到达,这最长可达主库上archive_timeout设置的时间。

如果某些参数在主库上被修改,备库上这些参数的设置也需要重新配置。对于这些参数,备库上的值必须等于或大于主库上的值。如果这些参数设置得不够高,备库将拒绝启动。此时可以提供更高的值并重启服务器以重新开始恢复。这些参数是:

  • max_connections

  • max_prepared_transactions

  • max_locks_per_transaction

管理员为max_standby_archive_delaymax_standby_streaming_delay选择合适的设置非常重要。最佳选择取决于业务优先级。例如,如果服务器的主要任务是充当高可用服务器,那么你会希望延迟设置较低,甚至可能设为零,尽管这是一个非常激进的设置。如果备库承担的是决策支持查询的附加服务器角色,那么把最大延迟设置为数小时甚至 -1(意味着永远等待查询完成)也可能是可以接受的。

主库上写出的事务状态“提示位” 不会被 WAL 记录,因此备库上的数据很可能会再次写出这些提示位。这样一来,即使所有用户都是只读的,备库仍然会执行磁盘写操作;不过数据值本身并不会发生变化。用户仍然会写出大型排序临时文件,并重新生成 relcache 信息文件,因此在热备模式下,数据库没有任何部分是真正只读的。还要注意,使用dblink模块写入远程数据库,以及借助 PL 函数执行其他数据库外部操作,依然是可能的,即使该事务在本地是只读的。

在恢复模式下,不接受下列类型的管理命令:

  • 数据定义语言(DDL):例如 CREATE INDEX

  • 权限和所有权:GRANTREVOKEREASSIGN

  • 维护命令:ANALYZEVACUUMCLUSTERREINDEX

再次注意,这些命令中的某些在主库上的“只读”事务中实际上是被允许的。

因此,你不能创建只存在于备库上的额外索引,也不能创建只存在于备库上的统计信息。如果需要这些管理命令,应在主库上执行,最终这些变更会传播到备库。

pg_cancel_backend()对用户后端有效,但对执行恢复的 Startup 进程无效。pg_stat_activity不会显示 Startup 进程的条目,恢复中的事务也不会显示为活动。因此,恢复期间pg_prepared_xacts总是空的。如果想解决存疑的预备事务,请查看主库上的pg_prepared_xacts并在那里发出命令解决事务。

与正常情况一样,pg_locks会显示后端持有的锁。pg_locks还会显示一个由启动进程管理的虚拟事务,它拥有所有正在被恢复重放的事务所持有的AccessExclusiveLocks。请注意,启动进程不会为了修改数据库而获取锁,因此除了AccessExclusiveLocks之外,其他锁不会在启动进程的pg_locks中显示;它们只是被假定存在。

Nagioscheck_pgsql插件可以工作,因为它检查的简单信息是存在的。check_postgres监控脚本也可以工作,尽管其中某些被报告的值可能会给出不同或令人困惑的结果。例如,最近一次清理时间不会被维护,因为备库上不会发生清理。不过,主库上执行的清理仍然会把其变更发送到备库。

恢复期间,WAL 文件控制命令不可用,例如pg_start_backuppg_switch_xlog等。

可动态载入的模块可以工作,包括pg_stat_statements

咨询锁在恢复期间可以正常工作,包括死锁检测。注意,咨询锁从不会被 WAL 记录,因此主库或备库上的咨询锁都不可能与 WAL 重放发生冲突。同样,也不可能在主库上获取一个咨询锁,却在备库上触发一个类似的咨询锁。咨询锁只与获取它们的那台服务器相关。

基于触发器的复制系统,如SlonyLondisteBucardo,根本无法在备库上运行;不过只要它们的变更不会被发送到备库并在那里应用,它们在主库上就可以正常工作。WAL 重放并不是基于触发器的,因此你不能把备库作为任何需要额外数据库写操作或依赖触发器的系统的中继节点。

不能分配新的 OID,不过某些UUID生成器仍可工作,只要它们不依赖于向数据库写入新的状态。

目前,在只读事务期间不允许创建临时表,因此某些现有脚本在这种情况下将无法正常运行。这个限制可能会在未来版本中放宽。这既涉及 SQL 标准兼容性问题,也涉及技术问题。

DROP TABLESPACE只有当表空间为空时才能成功。某些备库用户可能正通过其temp_tablespaces参数使用该表空间。如果表空间中存在临时文件,所有活动查询都会被取消,以确保临时文件被移除,这样表空间才能被删除,WAL 重放才能继续。

在主库上执行DROP DATABASEALTER DATABASE ... SET TABLESPACE会生成一条 WAL 记录,从而强制断开备库上所有连接到该数据库的用户。这个动作会立即发生,而不受max_standby_streaming_delay设置的影响。注意,ALTER DATABASE ... RENAME不会断开用户,这在大多数情况下不会被注意到,但如果程序依赖某种基于数据库名的机制,在某些情况下可能会导致混乱。

在普通(非恢复)模式下,如果你对一个具有登录能力的角色执行DROP USERDROP ROLE,而该用户仍然处于连接状态,那么已连接用户不会发生任何变化 — 他们会继续保持连接,不过之后不能重新连接。这种行为在恢复期间同样适用,因此在主库上执行一次DROP USER并不会断开备库上的该用户连接。

统计收集器在恢复期间保持活动状态。所有扫描、读取、块、索引使用情况等,都会在备库上正常记录。重放操作不会重复记录其在主库上产生的统计影响,因此重放一次插入不会增加 pg_stat_user_tables 的 Inserts 列。统计文件会在恢复开始时被删除,因此主库和备库的统计信息不同;这是特性,不是缺陷。

恢复期间自动清理不会运行。它会在恢复结束时正常启动。

检查点进程和后台写入进程在恢复期间是活动的。检查点进程会执行重启点(类似于主库上的检查点),后台写入进程会执行正常的块清理活动。这可能包括更新存储在备库上的提示位信息。恢复期间接受CHECKPOINT命令,不过它执行的是重启点,而不是新的检查点。

25.5.4. 热备参数参考 #

多个参数已经在第 25.5.2 节第 25.5.3 节中提到过。

在主库上,可以使用 wal_levelvacuum_defer_cleanup_age 参数。max_standby_archive_delaymax_standby_streaming_delay 在主库上设置时没有效果。

在备库上,可以使用 hot_standbymax_standby_archive_delaymax_standby_streaming_delay 参数。只要服务器仍处于备库模式,vacuum_defer_cleanup_age 就没有效果,不过备库成为主库后,它就会起作用。

25.5.5. 注意事项 #

热备存在若干限制。这些问题可以而且很可能会在未来的版本中修复:

  • 哈希索引上的操作目前不记入 WAL,因此重放不会更新这些索引。

  • 取快照之前需要完整了解正在运行的事务。使用大量子事务(当前为超过 64 个)的事务会延迟只读连接的开始,直到运行时间最长的写事务完成。如果发生这种情况,说明性消息将被发送到服务器日志。

  • 备库查询的有效起始点在主库的每个检查点处生成。如果在主库处于关闭状态时关闭备库,可能无法重新进入热备,直到主库重新启动并在 WAL 日志中生成更多起始点为止。在可能发生这种情况的最常见情形中,这并不是问题。通常,如果主库已被关闭并且不再可用,那多半是由于需要把备库转换为新主库运行的严重故障。如果主库是被有意停机的,那么协调确保备库平稳成为新主库也是标准流程。

  • 在恢复结束时,预备事务所持有的AccessExclusiveLocks将需要两倍于正常数目的锁表条目。如果计划运行大量并发且通常持有AccessExclusiveLocks的预备事务,或者计划让一个大事务持有多个AccessExclusiveLocks,建议为max_locks_per_transaction选择更大的值,也许可达主库上该参数值的两倍。如果把max_prepared_transactions设置为0,则完全无须考虑这一点。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。