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

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.1 已于 2016 年 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,也不能写系统预写式日志 to the system write-ahead log. Therefore, the following actions will produce error messages:

  • 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 的清理记录在应用时,会与那些快照仍然能够看到将被删除行的备库事务发生冲突。

  • 来自 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 记录的应用,它也可能使备库上的其他会话看不到主库上的最新变更。

一旦max_standby_archive_delaymax_standby_streaming_delay指定的延迟被超越,冲突查询将被取消。这通常仅导致一个取消错误,尽管在重放一个DROP DATABASE的情况下整个冲突会话都将被终止。另外,如果冲突发生在一个被空闲事务持有的锁上,该冲突会话会被终止(这种行为可能在未来被改变)。

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

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

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

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

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

如果备库查询被取消的次数多得令人无法接受,也存在补救办法。第一种选择是设置hot_standby_feedback参数,它会阻止VACUUM移除最近死亡的行,因此不会发生清理冲突。如果这样做,你应当注意这会延迟主库上死行的清理,从而可能导致不希望出现的表膨胀。不过,清理情况不会比直接在主库上运行这些备库查询更糟,而且你仍然可以获得把执行卸载到备库上的好处。在这种情况下必须保持较大的 max_standby_archive_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 打开为 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

Consistency information is recorded once per checkpoint on the primary. It is not possible to enable hot standby when reading WAL written during a period when wal_level was not set to hot_standby on the primary. Reaching a consistent state can also be delayed in the presence of both of these conditions:

  • A write transaction has more than 64 subtransactions

  • 存活时间很长的写事务

If you are running file-based log shipping ("warm standby"), you might need to wait until the next WAL file arrives, which could be as long as the archive_timeout setting on the primary.

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

  • 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,则完全不需要考虑这一点。

  • 热备中尚不支持可串行化事务隔离级别。(详见第 13.2.3 节第 13.4.1 节。)在热备模式下尝试把事务设置为可串行化隔离级别会产生错误。

提交更正

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