pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
持续归档可用于创建一种高可用性(HA)集簇配置,其中有一个或多个备库随时准备在主库失效时接管操作。这种能力通常称为温备或日志传送。
主库和备库协同工作以提供这种能力,不过两者之间只是松耦合。主库运行在持续归档模式下,而每台备库都运行在持续恢复模式下,从主库读取 WAL 文件。启用这种能力不需要修改数据库表,因此与其他一些复制方案相比,它的管理开销较低。这种配置对主库的性能影响也相对较小。
将 WAL 记录直接从一台数据库服务器移动到另一台数据库服务器,通常称为日志传送。PostgreSQL 通过一次传送一个文件(WAL 段)中的 WAL 记录来实现基于文件的日志传送。WAL 文件(16MB)可以轻松且低成本地传送到任意距离,无论是邻近系统、同一站点的另一台系统,还是地球另一端的系统。该技术所需的带宽取决于主库的事务速率。基于记录的日志传送粒度更细,会通过网络连接增量地流式传输 WAL 变更(见 第 25.2.5 节)。
需要注意的是,日志传送是异步的,也就是说 WAL 记录是在事务提交之后才被传送的。因此,如果主库发生灾难性故障,就会存在一个数据丢失窗口;尚未传送的事务将会丢失。对于基于文件的日志传送,可以通过使用 archive_timeout 参数来限制这个数据丢失窗口,它可以设置为低至数秒。不过,如此低的设置会显著增加文件传送所需的带宽。流复制(见 第 25.2.5 节)允许把数据丢失窗口缩得更小。
恢复性能已经足够好,因此一旦备库被激活,通常只需片刻就能达到完全可用状态。因此,这种配置被称为温备配置,它提供了高可用性。从归档的基础备份中恢复服务器并向前重放则需要更长时间,因此这种技术只能用于灾难恢复,而不是高可用性。备库还可以用于只读查询,这种情况下它被称为热备服务器。更多信息见 第 25.5 节。
通常,最好让主库和备库尽可能相似,至少从数据库服务器的角度看应如此。尤其是,与表空间相关的路径名会原样传递,因此如果使用该特性,主库和备库必须为表空间配置完全相同的挂载路径。请记住,如果在主库上执行 CREATE TABLESPACE,则它所需的任何新挂载点都必须在执行该命令之前先在主库和所有备库上创建好。硬件不必完全相同,但经验表明,在应用和系统的整个生命周期内,维护两个相同的系统要比维护两个不同的系统更容易。无论如何,硬件架构必须相同 — 例如,从 32 位系统向 64 位系统传送日志是不可行的。
一般来说,不能在运行不同主版本 PostgreSQL 的服务器之间传送日志。PostgreSQL 全球开发组的策略是在次版本升级期间不改变磁盘格式,因此主库和备库运行不同次版本通常也能正常工作。不过,这方面并没有正式支持,因此仍建议主库和备库尽量保持在相同的发行级别。当升级到新的次版本时,最安全的策略是先升级备库 — 新的次版本更有可能兼容读取前一个次版本生成的 WAL 文件,反过来则未必。
在备库模式下,服务器会持续应用从主库收到的 WAL。备库可以从 WAL 归档读取 WAL(参见 restore_command),也可以通过 TCP 连接直接从主库读取(流复制)。备库还会尝试恢复其集簇 pg_xlog 目录中找到的所有 WAL。这通常发生在服务器重启后,此时备库会重新重放重启前从主库接收的 WAL;不过,也可以随时手动将文件复制到 pg_xlog 中,让备库重放它们。
启动时,备库首先调用 restore_command,恢复归档位置中所有可用的 WAL。当读到归档中可用 WAL 的末尾,且 restore_command 失败后,它会尝试恢复 pg_xlog 目录中的所有可用 WAL。如果这也失败,且已配置流复制,备库就会尝试连接主库,从归档或 pg_xlog 中找到的最后一条有效记录开始接收 WAL 流。如果连接失败、未配置流复制,或连接后来断开,备库就会返回第一步,再次尝试从归档恢复文件。这种依次从归档、pg_xlog 和流复制重试的循环,会一直持续到服务器停止,或触发文件触发故障切换。
运行 pg_ctl promote 或发现触发文件(trigger_file)时,服务器会退出备库模式,切换到正常运行。在故障切换前,会恢复归档或 pg_xlog 中能立即获取的所有 WAL,但不会尝试连接主库。
如 第 24.3 节 所述,在主库上设置持续归档,将日志归档到备库可访问的归档目录。即使主库宕机,该归档位置也应该对备库可访问;也就是说,它应位于备库本身或另一台可信服务器上,而不是位于主库上。
如果想使用流复制,就需要在主库上配置认证,以允许来自备库的复制连接;也就是说,创建一个角色,并在 pg_hba.conf 中添加一个或多个把数据库字段设置为 replication 的合适项。还要确保主库配置文件中的 max_wal_senders 被设置为足够大的值。如果要使用复制槽,还应确保 max_replication_slots 也设置得足够高。
如 第 24.3.2 节 所述,获取一个基础备份来引导备库。
要配置备库,请恢复从主库取得的基础备份(参见 第 24.3.4 节)。在备库的集簇数据目录中创建恢复命令文件 recovery.conf,并启用 standby_mode。将 restore_command 设为一个从 WAL 归档复制文件的简单命令。如果计划使用多个备库提供高可用,请将 recovery_target_timeline 设为 latest,使备库能够跟随故障切换到另一个备库时发生的时间线变化。
不要将 pg_standby 或类似工具与这里介绍的内置备库模式一起使用。如果文件不存在,restore_command 应立即返回;服务器会在必要时重试该命令。有关 pg_standby 等工具的使用,参见 第 25.4 节。
如果希望使用流复制,请在 primary_conninfo 中填写 libpq 连接字符串,包含连接主库所需的主机名(或 IP 地址)及其他信息。如果主库使用密码认证,还需要在 primary_conninfo 中指定密码。
如果你是为了高可用目的设置备库,那么也应像主库一样设置 WAL 归档、连接和认证,因为故障切换后该备库将作为主库工作。
如果使用 WAL 归档,可以借助archive_cleanup_command 参数删除备库不再需要的文件,以尽量缩小归档大小。pg_archivecleanup 工具专门设计用于在典型的单备库配置中与 archive_cleanup_command 配合使用,见 pg_archivecleanup。不过请注意,如果你还把归档用于备份目的,即使这些文件对备库已经不再需要,也必须至少保留从最新基础备份恢复所需的那些文件。
一个简单的 recovery.conf 示例如下:
standby_mode = 'on' primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' restore_command = 'cp /path/to/archive/%f %p' archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'
备库的数量可以任意多,但如果使用流复制,请确保主库上的 max_wal_senders 设置得足够高,以允许它们同时连接。
与基于文件的日志传送相比,流复制可以让备库保持得更接近最新状态。备库连接到主库,主库在生成 WAL 记录时就会把它们流式发送给备库,而不必等待 WAL 文件被写满。
默认情况下流复制是异步的(见 第 25.2.8 节),在这种情况下主库上提交一个事务与该变化在备库上变得可见之间存在短暂的延迟。不过这种延迟比基于文件的日志传送方式中要小得多,在备库的能力足以跟得上负载的前提下,延迟通常低于一秒。在流复制中,不需要 archive_timeout 来缩减数据丢失窗口。
如果你使用流复制,但没有启用基于文件的持续归档,服务器可能会在备库收到旧的 WAL 段之前就把它们回收掉。如果发生这种情况,备库就需要重新通过新的基础备份进行初始化。可以通过把 wal_keep_segments 设置得足够大,以确保 WAL 段不会过早被回收,或者为备库配置一个复制槽,从而避免这种情况。如果配置了一个备库可访问的 WAL 归档,就不需要这些方案,因为只要归档保留了足够多的段,备库始终可以利用归档追赶上来。
要使用流复制,请先按 第 25.2 节 所述配置基于文件的日志传送备库。将这种备库转为流复制备库的关键步骤,是在 recovery.conf 文件中设置 primary_conninfo,使其指向主库。在主库上设置 listen_addresses 和认证选项(参见 pg_hba.conf),使备库能够连接主库的 replication 伪数据库(参见 第 25.2.5.1 节)。
在支持 keepalive 套接字选项的系统上,设置 tcp_keepalives_idle、tcp_keepalives_interval 和 tcp_keepalives_count 有助于主库迅速注意到断开的连接。
设置来自备库的最大并发连接数(详见 max_wal_senders)。
备库启动后,如果正确设置了 primary_conninfo,备库会在重放归档中所有可用的 WAL 文件后连接主库。如果连接成功,就会在备库上看到 walreceiver 进程,在主库上看到对应的 walsender 进程。
务必正确设置复制访问权限,使只有受信任的用户才能读取 WAL 流,因为很容易从中提取需要权限才能访问的信息。备库必须以超级用户或具有 REPLICATION 权限的账户向主库认证。建议为复制创建专用用户账户,并授予 REPLICATION 和 LOGIN 权限。虽然 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 md5
主库的主机名、端口号、连接用户名和密码在 recovery.conf 文件中指定。也可以将密码设置在备库的 ~/.pgpass 文件中(将 replication 指定为 database 字段的值)。例如,如果主库所在主机的 IP 地址为 192.168.1.50,端口为 5432,用于复制的账户名为 foo,密码为 foopass,管理员可以将以下行添加到 recovery.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'
流复制的一个重要健康指标,是主库上已经生成但尚未在备库上应用的 WAL 记录量。你可以通过比较主库上的当前 WAL 写入位置和备库收到的最后一个 WAL 位置来计算这种滞后。这些位置分别可以用主库上的 pg_current_xlog_location 和备库上的 pg_last_xlog_receive_location 取得(详见 表 9.65 和 表 9.66)。备库上的最后一个 WAL 接收位置也会显示在 WAL 接收进程的进程状态中,即通过 ps 命令显示的状态(详见 第 27.1 节)。
可以通过 pg_stat_replication 视图获取 WAL 发送进程列表。pg_current_xlog_location 与 sent_location 字段相差较大,可能表示主库负载很高;而 sent_location 与备库上 pg_last_xlog_receive_location 之间存在差异,则可能表示网络延迟或备库负载很高。
复制槽提供了一种自动化方法,以确保主库在所有备库都收到 WAL 段之前不会删除它们,并且即使备库处于断开状态,主库也不会删除那些一旦删除就可能导致恢复冲突的行。
除了使用复制槽,也可以通过 wal_keep_segments 防止旧 WAL 段被删除,或者使用 archive_command 将这些段保存在归档中。不过,这些方法通常会保留多于实际所需的 WAL 段,而复制槽只保留已知必需的段数。这些方法的优点是能限制 pg_xlog 的空间需求;目前使用复制槽还无法做到这一点。
类似地,hot_standby_feedback 和 vacuum_defer_cleanup_age 可以保护相关行,避免它们被清理删除,但前者在备库未连接期间无法提供保护,后者则通常需要设为较大的值才能提供充分保护。复制槽克服了这些缺点。
每个复制槽都有一个名称,该名称可以包含小写字母、数字和下划线字符。
现有复制槽及其状态可以在 pg_replication_slots 视图中查看。
复制槽可以通过流复制协议(见第 49.3 节)或者 SQL 函数(见第 9.26.6 节)创建和删除。
可以这样创建复制槽:
postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
slot_name | xlog_position
-------------+---------------
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 配置在备库的 recovery.conf 中。下面是一个简单的示例:
standby_mode = 'on' primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' primary_slot_name = 'node_a_slot'
级联复制特性允许一台备库接受复制连接,并像中继器一样把 WAL 记录流式发送给其他备库。这可以用来减少直接连接到主库的连接数,并使站点间的带宽开销最小化。
一台同时扮演接收者和发送者角色的备库称为级联备库。与主库连接更直接(经过更少级联备库)的备库称为上游服务器,而距离更远的备库称为下游服务器。级联复制并不限制下游服务器的数量和拓扑,不过每台备库只连接到一台上游服务器,而这条链路最终都会通向同一台主库。
级联备库不仅发送从主库接收到的 WAL 记录,也会发送那些从归档中恢复的记录。因此,即使某条上游复制连接被中断,只要仍有新的 WAL 记录可用,下游的流复制就会继续。
级联复制目前是异步的。同步复制(见第 25.2.8 节)设置当前对级联复制无影响。
热备反馈会向上传播,无论级联拓扑如何。
如果某个上游备库被提升为新主库,只要 recovery_target_timeline 设为 'latest',下游服务器就会继续从新主库接收流。
要使用级联复制,需要把级联备库设置为能够接受复制连接(也就是设置 max_wal_senders 和 hot_standby,并配置 基于主机的认证)。你还需要把下游备库中的 primary_conninfo 设置为指向级联备库。
PostgreSQL 的流复制默认是异步的。如果主库崩溃,则某些已提交的事务可能尚未复制到备库,从而导致数据丢失。数据丢失量与故障切换时的复制延迟成正比。
同步复制能够确认一个事务所做的全部修改已经被传送到一台同步备库。这扩展了事务提交所提供的标准持久性级别。在计算机科学理论中,这种保护级别被称为 2-safe 复制。
请求同步复制时,每个写事务的提交都会等待,直到收到确认,表明该提交已被写入主库和备库磁盘上的事务日志。数据唯一可能丢失的情况,是主库和备库同时崩溃。这可以提供更高的持久性级别,不过前提是系统管理员必须谨慎地部署和管理这两台服务器。等待确认会增强用户对服务器崩溃时更改不会丢失的信心,但也必然会增加请求事务的响应时间。最短等待时间是主库与备库之间的往返时间。
只读事务和事务回滚不需要等待备库的回应。子事务提交也不需要等待备库响应,只有顶层提交才需要等待。数据装载或索引构建等长时间运行的动作,直到最终提交时才会等待。所有两阶段提交操作都需要等待提交,包括准备和提交两个阶段。
一旦流复制已经配置好,配置同步复制只需要额外一步:必须把synchronous_standby_names设置为非空值。synchronous_commit也必须设置为on,但由于这是默认值,通常无需更改(见第 18.5.1 节和第 18.6.2 节)。这样的配置会导致每次提交都等待确认,以保证备库已经把提交记录写入持久存储。synchronous_commit可以由单个用户设置,因此既可以在配置文件中配置,也可以针对特定用户或数据库配置,或者由应用动态配置,从而在每事务级别控制持久性保证。
当提交记录已经在主库上写入磁盘之后,WAL 记录就会被发送到备库。每当新的一批 WAL 数据被写入磁盘时,备库就会发送回复消息,除非备库上的wal_receiver_status_interval被设置为零。如果根据主库上的synchronous_standby_names,该备库是第一个匹配的备库,那么它发出的回复将被用于唤醒那些正在等待确认提交记录已被收到的事务。这些参数允许管理员指定哪些备库应作为同步备库。注意,同步复制的配置主要在主库上进行。被命名的备库必须直接连接到主库;主库并不知道使用级联复制的下游备库。
把synchronous_commit设置为remote_write,会使每次提交都等待,直到备库确认已经收到提交记录并把它写入自己的操作系统,但不会等待数据被刷到备库磁盘上。与on相比,这种设置提供较弱一些的持久性保证:在操作系统崩溃时,备库可能丢失数据,尽管在PostgreSQL崩溃时不会。不过在实践中,这是一种有用的设置,因为它可以降低事务响应时间。只有当主库和备库都崩溃,并且主库数据库同时发生损坏时,才可能发生数据丢失。
如果请求快速关闭,用户将停止等待。不过,与使用异步复制时一样,在所有尚未传送的 WAL 记录传输到当前已连接的备库之前,服务器不会完全关闭。
同步复制通常要求对备库进行仔细规划和部署,才能保证应用具有可接受的性能。等待本身不会占用系统资源,但事务锁会一直保持到传输得到确认。因此,若不谨慎使用同步复制,数据库应用的性能会因为响应时间增加和争用加剧而下降。
PostgreSQL允许应用开发者通过复制来指定所需的持久性级别。这可以在整个系统级别指定,也可以针对特定用户、特定连接,甚至单个事务指定。
例如,一个应用负载可能由如下部分组成:10% 的变更是重要的客户资料,而 90% 的变更是不太重要、即使丢失业务也较容易承受的数据,例如用户之间的聊天消息。
通过在应用级别(在主库上)指定同步复制选项,我们可以只对最重要的变更提供同步复制,而不会拖慢大部分工作负载。应用级别选项是让高性能应用获得同步复制收益的一种重要而实用的工具。
你应当确保网络带宽高于 WAL 数据的生成速率。
当synchronous_commit设置为on或remote_write时,事务提交将等待,直到同步备库做出响应。如果最后一台(或唯一一台)同步备库崩溃,这种响应可能永远不会到来。
对于避免数据丢失而言,最好的办法是确保你不会失去最后一台剩余的同步备库。这可以通过在synchronous_standby_names中命名多个潜在同步备库来实现。第一台被命名的备库将被用作同步备库。列在它之后的备库,会在第一台失效时接替同步备库的角色。
当一台备库第一次连接到主库时,它还没有正确同步。这种状态称为catchup模式。一旦备库与主库之间的滞后第一次变为零,它就会进入实时的streaming状态。备库刚创建之后,追赶阶段可能会持续较长时间。如果备库被关闭,则追赶阶段会随着它停机时间的延长而变长。只有在到达streaming状态后,备库才能成为同步备库。
如果主库在提交正等待确认时重启,这些等待中的事务会在主库恢复后被标记为已完全提交。无法确定在主库崩溃时,所有备库是否已经收到全部待传送的 WAL 数据。因此,某些事务可能不会在备库上显示为已提交,即使它们在主库上显示为已提交。我们所提供的保证是:只有在确认 WAL 数据已经被该备库安全接收之后,应用才会收到事务成功提交的显式确认。
如果你确实失去了最后一台备库服务器,那么就应当禁用synchronous_standby_names,然后在主库上重新加载配置文件。
如果主库与剩余的备库隔离开了,你应当故障切换到那些剩余备库中最佳的候选者。
如果需要在事务等待期间重新创建备库,请确保在 synchronous_commit = off 的会话中运行 pg_start_backup() 和 pg_stop_backup() 命令,否则这些请求会一直等待备库出现。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。