受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

26.2. 日志传送备库 #

连续归档可用于创建一种高可用性(HA)集簇配置,其中有一个或多个备库随时准备在主库失效时接管操作。这种能力通常称为温备日志传送

主库和备库协同工作以提供这种能力,不过两者之间只是松耦合。主库运行在持续归档模式下,而每台备库都运行在持续恢复模式下,从主库读取 WAL 文件。启用这种能力不需要修改数据库表,因此与其他一些复制方案相比,它的管理开销较低。这种配置对主库的性能影响也相对较小。

将 WAL 记录直接从一台数据库服务器移动到另一台数据库服务器,通常称为日志传送。PostgreSQL 通过一次传送一个文件(WAL 段)中的 WAL 记录来实现基于文件的日志传送。WAL 文件(16MB)可以轻松且低成本地传送到任意距离,无论是邻近系统、同一站点的另一台系统,还是地球另一端的系统。该技术所需的带宽取决于主库的事务速率。基于记录的日志传送粒度更细,会通过网络连接增量地流式传送 WAL 变更(见 Section 26.2.5)。

需要注意的是,日志传送是异步的,也就是说 WAL 记录是在事务提交之后才被传送的。因此,如果主库发生灾难性故障,就会存在一个数据丢失窗口;尚未传送的事务将会丢失。对于基于文件的日志传送,可以通过使用 archive_timeout 参数来限制这个数据丢失窗口,它可以设置为低至数秒。不过,如此低的设置会显著增加文件传送所需的带宽。流复制(见 Section 26.2.5)允许把数据丢失窗口缩得更小。

恢复性能已经足够好,因此一旦备库被激活,通常只需片刻就能达到完全可用状态。因此,这种配置被称为温备配置,它提供了高可用性。从归档的基础备份中恢复服务器并向前重放则需要更长时间,因此这种技术只能用于灾难恢复,而不是高可用性。备库还可以用于只读查询,这种情况下它被称为热备服务器。更多信息见 Section 26.4

26.2.1. 规划 #

通常,最好让主库和备库尽可能相似,至少从数据库服务器的角度看应如此。尤其是,与表空间相关的路径名会原样传递,因此如果使用该特性,主库和备库必须为表空间配置完全相同的挂载路径。请记住,如果在主库上执行 CREATE TABLESPACE,则它所需的任何新挂载点都必须在执行该命令之前先在主库和所有备库上创建好。硬件不必完全相同,但经验表明,在应用和系统的整个生命周期内,维护两个相同的系统要比维护两个不同的系统更容易。无论如何,硬件架构必须相同 — 例如,从 32 位系统向 64 位系统传送日志是不可行的。

一般来说,不能在运行不同主版本 PostgreSQL 的服务器之间传送日志。PostgreSQL 全球开发组的策略是在次版本升级期间不改变磁盘格式,因此主库和备库运行不同次版本通常也能正常工作。不过,这方面并没有正式支持,因此仍建议主库和备库尽量保持在相同的发行级别。当升级到新的次版本时,最安全的策略是先升级备库 — 新的次版本更有可能兼容读取前一个次版本生成的 WAL 文件,反过来则未必。

26.2.2. 备库操作 #

如果服务器启动时其数据目录中存在 standby.signal 文件,服务器就会进入备库模式。

在备库模式中,服务器会持续应用从主库接收到的 WAL。备库可以从 WAL 归档中读取 WAL(见 restore_command),也可以通过 TCP 连接直接从主库读取 WAL(流复制)。备库还会尝试恢复位于备库集簇 pg_wal 目录中的任何 WAL。这种情况通常发生在服务器重启之后,此时备库会再次重放重启前从主库流入的 WAL;不过你也可以在任何时候手动把文件复制到 pg_wal 中,以便让它们被重放。

启动时,备库首先通过调用 restore_command 恢复归档位置中所有可用的 WAL。一旦到达其中可用 WAL 的末尾,并且 restore_command 失败,它就会尝试恢复 pg_wal 目录中可用的任何 WAL。如果这也失败,并且已经配置了流复制,备库就会尝试连接到主库,并从归档或 pg_wal 中找到的最后一条有效记录开始流式接收 WAL。如果这仍然失败,或者没有配置流复制,或者连接后来断开,备库就会返回第 1 步,再次尝试从归档恢复文件。这样在归档、pg_wal 以及流复制之间循环重试,直到服务器停止或被提升。

当执行 pg_ctl promote 或调用 pg_promote() 时,将退出备库模式,服务器切换到正常运行。在故障转移之前,会先恢复归档中或 pg_wal 中立即可用的所有 WAL,但不会尝试连接主库。

26.2.3. 为备库准备主库 #

Section 25.3 所述,在主库上设置持续归档,将日志归档到备库可访问的归档目录。即使主库宕机,该归档位置也应该对备库可访问;也就是说,它应位于备库本身或另一台可信服务器上,而不是位于主库上。

如果想使用流复制,就需要在主库上配置认证,以允许来自备库的复制连接;也就是说,创建一个角色,并在 pg_hba.conf 中添加一个或多个把数据库字段设置为 replication 的合适项。还要确保主库配置文件中的 max_wal_senders 被设置为足够大的值。如果要使用复制槽,还应确保 max_replication_slots 也设置得足够高。

Section 25.3.2 所述,获取一个基础备份来引导备库。

26.2.4. 设置备库 #

要设置备库,先恢复从主库获取的基础备份(见 Section 25.3.5)。然后在备库的集簇数据目录中创建一个 standby.signal 文件。把 restore_command 设置为一个从 WAL 归档复制文件的简单命令。如果你打算出于高可用目的设置多个备库,请确认 recovery_target_timeline 被设置为 latest(默认值),这样该备库就会跟随故障转移到另一台备库后产生的时间线变化。

Note

如果文件不存在,restore_command应立即返回;服务器会在必要时再次尝试执行该命令。

如果想使用流复制,就在 primary_conninfo 中填入一个 libpq 连接字符串,其中包含主机名(或 IP 地址)以及连接主库所需的其他细节。如果主库认证需要密码,也应在 primary_conninfo 中指定该密码。

如果你是为了高可用目的设置备库,那么也应像主库一样设置 WAL 归档、连接和认证,因为故障转移后该备库将作为主库工作。

如果使用 WAL 归档,可以借助archive_cleanup_command 参数删除备库不再需要的文件,以尽量缩小归档大小。pg_archivecleanup 工具专门设计用于在典型的单备库配置中与 archive_cleanup_command 配合使用,见 pg_archivecleanup。不过请注意,如果你还把归档用于备份目的,即使这些文件对备库已经不再需要,也必须至少保留从最新基础备份恢复所需的那些文件。

一个简单的配置示例如下:

primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass options=''-c wal_sender_timeout=5000'''
restore_command = 'cp /path/to/archive/%f %p'
archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'

备库的数量可以任意多,但如果使用流复制,请确保主库上的 max_wal_senders 设置得足够高,以允许它们同时连接。

26.2.5. 流复制 #

与基于文件的日志传送相比,流复制可以让备库保持得更接近最新状态。备库连接到主库,主库在生成 WAL 记录时就会把它们流式发送给备库,而不必等待 WAL 文件被写满。

默认情况下流复制是异步的(见 Section 26.2.8),在这种情况下主库上提交一个事务与该变化在备库上变得可见之间存在短暂的延迟。不过这种延迟比基于文件的日志传送方式中要小得多,在备库的能力足以跟得上负载的前提下,延迟通常低于一秒。在流复制中,不需要 archive_timeout 来缩减数据丢失窗口。

如果你使用流复制,但没有启用基于文件的持续归档,服务器可能会在备库收到旧的 WAL 段之前就把它们回收掉。如果发生这种情况,备库就需要重新通过新的基础备份进行初始化。可以通过把 wal_keep_size 设置得足够大,以确保 WAL 段不会过早被回收,或者为备库配置一个复制槽,从而避免这种情况。如果配置了一个备库可访问的 WAL 归档,就不需要这些方案,因为只要归档保留了足够多的段,备库始终可以利用归档追赶上来。

要使用流复制,请先按 Section 26.2 所述设置一个基于文件的日志传送备库。把它转变为流复制备库的步骤,就是把 primary_conninfo 设置为指向主库。还要在主库上设置 listen_addresses 和认证选项(见 pg_hba.conf),以便备库能够连接到主库上的 replication 伪数据库(见 Section 26.2.5.1)。

在支持 keepalive 套接字选项的系统上,设置 tcp_keepalives_idletcp_keepalives_intervaltcp_keepalives_count 有助于主库迅速注意到断开的连接。

设置来自备库的最大并发连接数(详见 max_wal_senders)。

当备库启动并且 primary_conninfo 设置正确后,它会在重放完归档中所有可用的 WAL 文件之后连接到主库。如果连接成功建立,你会在备库上看到一个 walreceiver,并在主库上看到相应的 walsender 进程。

26.2.5.1. 认证 #

正确设置复制访问权限非常重要,因为从 WAL 流中很容易提取出需要权限才能访问的信息,因此必须确保只有受信任的用户才能读取它。备库必须以具有 REPLICATION 权限的账户或超级用户身份向主库认证。建议专门创建一个具有 REPLICATIONLOGIN 权限的用户账户用于复制。虽然 REPLICATION 权限的权限很高,但它并不允许用户修改主系统上的任何数据,而 SUPERUSER 权限则允许。

复制的客户端认证由 pg_hba.conf 中的一条记录控制,该记录需要把 replication 指定在 database 字段中。例如,如果备库运行在主机 IP 192.168.1.100 上,并且用于复制的账户名为 foo,管理员可以在主库上的 pg_hba.conf 文件中加入下列行:

# Allow the user "foo" from host 192.168.1.100 to connect to the primary
# as a replication standby if the user's password is correctly supplied.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    replication     foo             192.168.1.100/32        scram-sha-256

主库的主机名和端口号、连接用户名以及密码都在 primary_conninfo 中指定。密码也可以设置在备库上的 ~/.pgpass 文件中(把 replication 指定在 database 字段中)。例如,如果主库运行在主机 IP 192.168.1.50、端口 5432 上,用于复制的账户名为 foo,并且密码是 foopass,管理员可以在备库的 postgresql.conf 文件中加入下列行:

# The standby connects to the primary that is running on host 192.168.1.50
# and port 5432 as the user "foo" whose password is "foopass".
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'

26.2.5.2. 监控 #

流复制的一个重要健康指标,是主库上已经生成但尚未在备库上应用的 WAL 记录量。你可以通过比较主库上的当前 WAL 写入位置和备库收到的最后一个 WAL 位置来计算这种滞后。这些位置分别可以用主库上的 pg_current_wal_lsn 和备库上的 pg_last_wal_receive_lsn 取得(详见 Table 9.91Table 9.92)。备库上的最后一个 WAL 接收位置也会显示在 WAL 接收进程的进程状态中,即通过 ps 命令显示的状态(详见 Section 27.1)。

你可以通过 pg_stat_replication 视图取得 WAL 发送进程列表。pg_current_wal_lsn 与该视图的 sent_lsn 字段之间存在较大差异,可能表示主库负载很重;而 sent_lsn 与备库上的 pg_last_wal_receive_lsn 之间存在较大差异,则可能表示网络延迟,或者备库负载很重。

在热备上,WAL 接收进程的状态可以通过 pg_stat_wal_receiver视图取得。pg_last_wal_replay_lsn与该视图的flushed_lsn之间存在较大差异,表示 WAL 的接收速度快于它被重放的速度。

26.2.6. 复制槽 #

复制槽提供了一种自动化方法,以确保主库在所有备库都收到 WAL 段之前不会删除它们,并且即使备库处于断开状态,主库也不会删除可能导致恢复冲突的行。

如果不使用复制槽,也可以通过wal_keep_size阻止旧的 WAL 段被移除,或者通过archive_commandarchive_library把这些段保存到归档中。这些方法的一个缺点是,它们通常会保留多于实际所需数量的 WAL 段,而复制槽只会保留已知确实需要的段数。

类似地,如果不配合复制槽单独使用 hot_standby_feedback,它虽然能防止相关的行被清理而避免因清理产生的问题,但在备库未连接的那段时间内并不能提供保护。

Caution

注意,复制槽可能会导致服务器保留过多的 WAL 段,以至于占满分配给pg_wal的空间。可以使用max_slot_wal_keep_size来限制复制槽保留的 WAL 文件大小。

26.2.6.1. 查询和管理复制槽 #

每个复制槽都有一个名称,该名称可以包含小写字母、数字和下划线字符。

现有复制槽及其状态可以在 pg_replication_slots 视图中查看。

复制槽可以通过流复制协议(见Section 53.4)或者 SQL 函数(见Section 9.27.6)创建和删除。

26.2.6.2. 配置示例 #

你可以像下面这样创建一个复制槽:

postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
  slot_name  | lsn
-------------+-----
 node_a_slot |

postgres=# SELECT slot_name, slot_type, active FROM pg_replication_slots;
  slot_name  | slot_type | active
-------------+-----------+--------
 node_a_slot | physical  | f
(1 row)

要让备库使用这个槽,应在备库上配置 primary_slot_name。下面是一个简单示例:

primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
primary_slot_name = 'node_a_slot'

26.2.7. 级联复制 #

级联复制特性允许一台备库接受复制连接,并像中继器一样把 WAL 记录流式发送给其他备库。这可以用来减少直接连接到主库的连接数,并使站点间的带宽开销最小化。

一台同时扮演接收者和发送者角色的备库称为级联备库。与主库连接更直接(经过更少级联备库)的备库称为上游服务器,而距离更远的备库称为下游服务器。级联复制并不限制下游服务器的数量和拓扑,不过每台备库只连接到一台上游服务器,而这条链路最终都会通向同一台主库。

级联备库不仅发送从主库接收到的 WAL 记录,也会发送那些从归档中恢复的记录。因此,即使某条上游复制连接被中断,只要仍有新的 WAL 记录可用,下游的流复制就会继续。

级联复制目前是异步的。同步复制(见Section 26.2.8)设置当前对级联复制无影响。

热备反馈会向上传播,无论级联拓扑如何。

如果一台上游备库被提升为新的主库,并且下游服务器的 recovery_target_timeline 被设置为 'latest'(默认值),下游服务器将继续从新的主库接收流。

要使用级联复制,需要把级联备库设置为能够接受复制连接(也就是设置 max_wal_sendershot_standby,并配置 基于主机的认证)。你还需要把下游备库中的 primary_conninfo 设置为指向级联备库。

26.2.8. 同步复制 #

PostgreSQL 的流复制默认是异步的。如果主库崩溃,则某些已提交的事务可能尚未复制到备库,从而导致数据丢失。数据丢失量与故障转移时的复制延迟成正比。

同步复制能够确认一个事务所做的全部修改已经被传送到一台或多台同步备库。这扩展了事务提交所提供的标准持久性级别。在计算机科学理论中,这种保护级别被称为 2-safe 复制;而当synchronous_commit被设置为remote_write时,则称为 group-1-safe(group-safe 和 1-safe)。

请求同步复制时,每个写事务的提交都会等待,直到收到确认,表明该提交已被写入主库和备库磁盘上的预写式日志。数据唯一可能丢失的情况,是主库和备库同时崩溃。这可以提供更高的持久性级别,不过前提是系统管理员必须谨慎地部署和管理这两台服务器。等待确认会增强用户对服务器崩溃时更改不会丢失的信心,但也必然会增加请求事务的响应时间。最短等待时间是主库与备库之间的往返时间。

只读事务和事务回滚不需要等待备库的回应。子事务提交也不需要等待备库响应,只有顶层提交才需要等待。数据装载或索引构建等长时间运行的动作,不会一直等到最终提交消息。所有两阶段提交操作都需要等待提交,包括 prepare 和 commit。

同步备库可以是物理复制备库,也可以是逻辑复制订阅者。它还可以是任何其他物理或逻辑 WAL 复制流的消费者,只要它知道如何发送适当的反馈消息。除了内置的物理和逻辑复制系统之外,还包括pg_receivewalpg_recvlogical之类的专用程序,以及一些第三方复制系统和定制程序。关于同步复制支持的细节,请查看相应文档。

26.2.8.1. 基本配置 #

一旦流复制已经配置好,配置同步复制只需要额外一步:必须把synchronous_standby_names设置为非空值。synchronous_commit也必须设置为on,但由于这是默认值,通常无需更改(见Section 19.5.1Section 19.6.2)。这样的配置会导致每次提交都等待确认,以保证备库已经把提交记录写入持久存储。synchronous_commit可以由单个用户设置,因此既可以在配置文件中配置,也可以针对特定用户或数据库配置,或者由应用动态配置,从而在每事务级别控制持久性保证。

当提交记录已经在主库上写入磁盘之后,WAL 记录就会被发送到备库。每当新的一批 WAL 数据被写入磁盘时,备库就会发送回复消息,除非备库上的wal_receiver_status_interval被设置为零。如果synchronous_commit被设置为remote_apply,那么备库会在提交记录被重放时发送回复消息,从而使该事务变得可见。如果根据主库上的synchronous_standby_names设置,该备库被选为同步备库,那么它发出的回复会与其他同步备库的回复一起,用于决定何时释放那些正在等待确认提交记录已被收到的事务。这些参数允许管理员指定哪些备库应作为同步备库。注意,同步复制的配置主要在主库上进行。被命名的备库必须直接连接到主库;主库并不知道使用级联复制的下游备库。

synchronous_commit设置为remote_write,会使每次提交都等待,直到备库确认已经收到提交记录并把它写入自己的操作系统,但不会等待数据被刷到备库磁盘上。与on相比,这种设置提供较弱一些的持久性保证:在操作系统崩溃时,备库可能丢失数据,尽管在PostgreSQL崩溃时不会。不过在实践中,这是一种有用的设置,因为它可以降低事务响应时间。只有当主库和备库都崩溃,并且主库数据库同时发生损坏时,才可能发生数据丢失。

synchronous_commit设置为remote_apply,会使每次提交都等待,直到当前同步备库报告它们已经重放了该事务,从而使它对用户查询可见。在简单场景下,这可以支持具备因果一致性的负载均衡。

如果请求快速关闭,用户将停止等待。不过,与使用异步复制时一样,在所有尚未解决的 WAL 记录传输到当前已连接的备库之前,服务器不会完全关闭。

26.2.8.2. 多个同步备库 #

同步复制支持一台或多台同步备库;事务将一直等待,直到所有被视为同步的备库确认已收到其数据。事务需要等待多少台同步备库的回复,由synchronous_standby_names指定。该参数还指定了一个备库名称列表,以及从中选择同步备库的方法(FIRSTANY)。

FIRST方法指定基于优先级的同步复制,使事务提交等待,直到它们的 WAL 记录被复制到按优先级选出的、所要求数量的同步备库上。列表中出现得越早的备库优先级越高,并会被视为同步备库。该列表中更靠后的备库则是潜在的同步备库。如果当前某个同步备库因任何原因断开连接,它将立刻由下一个优先级最高的备库替代。

基于优先级的多同步备库的synchronous_standby_names示例如下:

synchronous_standby_names = 'FIRST 2 (s1, s2, s3)'

在这个例子中,如果四台备库s1s2s3s4都在运行,则s1s2会被选为同步备库,因为它们的名字在备库名称列表中出现得更早。s3是潜在的同步备库,当s1s2中的任意一台失效时,它就会接替其角色。由于s4的名称不在列表中,因此它是异步备库。

ANY方法指定基于法定人数的同步复制,使事务提交等待,直到它们的 WAL 记录被复制到列表中所要求数量的同步备库中的至少这么多台。

基于法定人数的多同步备库的synchronous_standby_names示例如下:

synchronous_standby_names = 'ANY 2 (s1, s2, s3)'

在这个例子中,如果四台备库s1s2s3s4都在运行,事务提交将等待来自s1s2s3中的任意两台备库的回复。由于s4的名字不在列表中,因此它是异步备库。

备库的同步状态可以通过pg_stat_replication视图查看。

26.2.8.3. 性能规划 #

同步复制通常要求对备库进行仔细规划和部署,才能保证应用具有可接受的性能。等待本身不会占用系统资源,但事务锁会一直保持到传输得到确认。因此,若不谨慎使用同步复制,数据库应用的性能会因为响应时间增加和争用加剧而下降。

PostgreSQL允许应用开发者通过复制来指定所需的持久性级别。这可以在整个系统级别指定,也可以针对特定用户、特定连接,甚至单个事务指定。

例如,一个应用负载可能由如下部分组成:10% 的变更是重要的客户资料,而 90% 的变更是不太重要、即使丢失业务也较容易承受的数据,例如用户之间的聊天消息。

通过在应用级别(在主库上)指定同步复制选项,我们可以只对最重要的变更提供同步复制,而不会拖慢整体工作负载。应用级别选项是让高性能应用获得同步复制收益的一种重要而实用的工具。

你应当确保网络带宽高于 WAL 数据的生成速率。

26.2.8.4. 高可用规划 #

synchronous_standby_names指定了当synchronous_commit设置为onremote_applyremote_write时,事务提交需要等待其响应的同步备库的数量和名称。如果任意一台同步备库崩溃,此类事务提交就可能永远无法完成。

对于高可用而言,最好的办法是确保你始终保有所要求数量的同步备库。这可以通过在synchronous_standby_names中命名多个潜在同步备库来实现。

在基于优先级的同步复制中,列表中出现较早的备库将被用作同步备库。列在它们之后的备库,会在当前同步备库失效时接替其角色。

在基于法定人数的同步复制中,列表中的所有备库都会被用作同步备库候选。即使其中一台失效,其他备库仍会继续承担同步备库候选的角色。

当一台备库第一次连接到主库时,它还没有正确同步。这种状态称为catchup模式。一旦备库与主库之间的滞后第一次变为零,它就会进入实时的streaming状态。备库刚创建之后,追赶阶段可能会持续较长时间。如果备库被关闭,则追赶阶段会随着它停机时间的延长而变长。只有在到达streaming状态后,备库才能成为同步备库。这种状态可以通过pg_stat_replication视图查看。

如果主库在提交正等待确认时重启,这些等待中的事务会在主库恢复后被标记为已完全提交。无法确定在主库崩溃时,所有备库是否已经收到全部尚未处理的 WAL 数据。因此,某些事务可能不会在备库上显示为已提交,即使它们在主库上显示为已提交。我们所提供的保证是:只有在确认 WAL 数据已经被所有同步备库安全接收之后,应用才会收到事务成功提交的显式确认。

如果你确实无法维持所要求数量的同步备库,那么就应当在synchronous_standby_names中减少事务提交需要等待其响应的同步备库数量(或者禁用它),然后在主库上重新加载配置文件。

如果主库与剩余的备库隔离开了,你应当故障转移到那些剩余备库中最佳的候选者。

如果你需要在事务正在等待时重新创建一台备库,请确保pg_backup_start()pg_backup_stop()是在一个把synchronous_commit设置为off的会话中运行的,否则这些请求将永远等待备库出现。

26.2.9. 在备库中持续归档 #

当在备库中使用持续 WAL 归档时,有两种不同的场景:WAL 归档可以由主库和备库共享,或者备库可以拥有自己的 WAL 归档。当备库拥有自己的 WAL 归档时,应把archive_mode设置为always,这样备库就会为它接收到的每个 WAL 段调用归档命令,无论该 WAL 段是通过从归档恢复得到的,还是通过流复制得到的。共享归档也可以类似处理,但archive_commandarchive_library必须检查正在归档的文件是否已经存在,以及已有文件的内容是否完全相同。这就要求在archive_commandarchive_library中更加小心:既不能用不同内容覆盖现有文件,又要在同一个文件被归档两次且内容完全相同时返回成功。如果两台服务器同时尝试归档同一个文件,还必须确保整个过程不存在竞争条件。

如果archive_mode被设置为on,那么归档器在恢复期间或备库模式下不会启用。如果备库被提升,它会在提升后开始归档,但不会归档任何不是由它自己生成的 WAL 或时间线历史文件。要在归档中获得完整的一系列 WAL 文件,就必须确保所有 WAL 在到达备库之前已经被归档。对于基于文件的日志传送,这天然成立,因为备库只能恢复归档中找到的文件;但在启用流复制时则不是这样。当服务器不处于恢复模式时,onalways模式之间没有区别。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。