• [译] 11. 流复制


    [译] 11. 流复制

    原文地址:https://www.interdb.jp/pg/pgsql11.html

    原文作者:Hironobu SUZUKI

    在9.1版本中实现了同步流复制。即所谓的单主多从(single-master-multi-slaves)类型复制,这两个术语-主(master)和从(slave(s)),在PostgreSQL中通常分别称为主(primary)和备(standby(s))。

    这种本机复制功能基于日志传送,这是一种通用复制技术,其中主服务器持续地发送 WAL 数据,然后每个备用服务器立即重放接收到的WAL数据。

    本章涵盖以下主题,重点介绍流复制的工作原理:

    • 流复制如何启动
    • 数据如何在主服务器和备用服务器之间传输
    • 主服务器如何管理多个备用服务器
    • 主服务器如何检测备用服务器的故障

    虽然第一个复制功能(仅用于异步复制)已在 9.0 版中实现,但它已在 9.1 中替换为用于同步复制的新实现(当前正在使用)。

    11.1 启动流复制

    在流复制环境中,有三类进程配合工作。在主库的walsender 进程将wal数据发送给备库;然后备库上的walreceiverstartup 进程接收和重放这些数据。walsender 和 walreceiver 间使用单TCP连接进行通信。

    Fig. 11.1. SR startup sequence.

    Fig. 11.1. SR startup sequence.

    (1) 启动主(primary)和备(standby)服务器

    (2) 备库(standby)启动 startup 进程

    (3) 备库(standby)启动 walreceiver 进程

    (4) walreceiver 进程向主库发送一个连接请求。如果主库没有运行,walreceiver 就会定期发送这些请求。

    (5) 当主库(primary)接收到连接请求后,它启动一个 walsender 进程并在walsender 和 walreceiver 之间建立一个TCP连接

    (6) walreceiver 发送备用(standby)数据库集簇最新的LSN。一般来说,这个阶段在信息科学领域称为握手(handshaking)。

    (7) 如果备库(standby)最新LSN小于主库(primary) 最新LSN(Standby's LSN < Primary's LSN),walsender 进程将备库最新LSN前一个LSN至其后的LSN发送WAL数据到备库。这些WAL数据由存储在主库的pg_xlog 子目录(10或更高版本pg_wal子目录) 中的WAL段文件提供。然后,备库重放接收到的WAL数据。在这个阶段,备库追赶主库的过程,称为"catch-up"。

    (8) 流复制开始工作

    每个 walsender 进程保持一个适合连接 walreceiver 或任何应用程序工作阶段的状态。(请注意,他不是 walreceiver 或 连接到 walsender 的应用程序的状态)。以下是一些可能的状态:

    • start-up : 从启动 walsender 到结束 handshaking。见图11.1(5)-(6)
    • catch-up : 在catch-up阶段。图11.1(7)
    • streaming: 流复制工作期间。图11.1(8)
    • backup: 为备份工具,如pg_basebackup 使用程序发送整个数据库集簇文件期间段。

    pg_stat_replication 视图显示了所有运行的walsender的状态。示例如下所示:

    testdb=# SELECT application_name,state FROM pg_stat_replication;
     application_name |   state
    ------------------+-----------
     standby1         | streaming
     standby2         | streaming
     pg_basebackup    | backup
    (3 rows)
    

    如上面的结果所示,两个 walsender 正在运行,为连接的备库发送 WAL 数据,另一个是为 pg_basebackup 实用程序发送数据库集簇的所有文件。

    如果备库停止很长时间后重新启动会发生什么?

    在 9.3 或更早的版本中,如果备库所需的WAL 段文件在主库上已经被回收,则备库无法赶上主库。这个问题没有可靠的解决方案,只能将配置参数wal_keep_segments 设置一个较大的值,以减少发生的可能性。这是一个权宜之计。

    在 9.4 或更高版本中,可以使用复制槽来防止此类问题。复制槽是一个扩展 WAL 数据发送灵活性的特性,主要用于逻辑复制,它也提供了解决这个问题的方法—— 可以通过暂停回收过程将 pg_xlog(或 pg_wal 如果是 10 或更高版本)下包含未发送数据的 WAL 段文件保留在复制槽中。详细请参考官方文档

    11.2 流复制工作原理

    流复制有两方面:日志传输和数据库同步。日志传送显然是其中之一,因为流复制依赖它——只要发生写入,主库就会将WAL数据发送给连接的备库。同步复制需要同步数据库——主库与每个多备(multiple-standby)通信以同步其数据库集簇。

    为了准确理解流复制的工作原理,我们应该探索一台主服务器如何管理多台备用服务器。为简单起见,本节介绍特殊场景(即单主单备系统),而下一节将介绍通用场景(单主多备系统)。

    11.2.1 主库和同步备库之间的通信

    假设备用服务器处于同步复制模式,但配置参数 hot-standby 为 disable,wal_level 为'archive'。主服务器的主要参数如下图所示:

    synchronous_standby_names = 'standby1'
    hot_standby = off
    wal_level = archive
    

    此外,在 9.5 节 中提到的写入 WAL 数据的三个触发器中,我们这里重点关注事务提交。

    假设主库上的一个后端进程以自动提交模式发出一个简单的 INSERT 语句。后端启动事务,执行 INSERT 语句,然后立即提交事务。让我们进一步探讨此提交操作是如何完成的。请参见下面图 11.2 中的顺序:

    Fig. 11.2. Streaming Replication's communication sequence diagram.

    Fig. 11.2. Streaming Replication's communication sequence diagram.

    (1) 后端进程通过执行XLogInsert()XLogFlush()函数将WAL数据写入并刷新到WAL段文件中

    (2) walsender进程将已写入WAL段文件的WAL数据发送给walreceiver进程

    (3) 发送WAL数据后,后端进程继续等待备库的 ACK 响应。更准确地说,后端进程通过执行内部函数 SyncRepWaitForLSN() 获得一个latch,并等待它被释放。

    (4) 备库上的 walreceiver 通过 write() 系统调用函数将接收到的 WAL 数据写入备库的 WAL 段中,并向 walsender 返回一个 ACK 响应。

    (5) walreceiver 使用 fsync() 等系统调用将 WAL 数据刷新到 WAL 段,向 walsender 返回另一个 ACK 响应,并通知startup进程更新 WAL 数据。

    (6) startup 进程重放已写入WAL段的WAL数据

    (7) walsender 在收到 walreceiver 的 ACK 响应后释放后端进程的latch,然后,后端进程的提交或中止操作将完成。latch 锁释放的时间取决于synchronous_commit参数。值为'on'(默认)时,表示当收到步骤(5)的ACK时释放latch 锁;值为'remote_write'时 ,表示当收到步骤(4)的ACK时释放latch 锁。

    如果配置参数 wal_level 是 'hot_standby' 或 'logical',PostgreSQL 会在提交或中止操作的记录之后写入有关hot standby功能的 WAL 记录。 (在本例中,PostgreSQL 不写入该记录,因为它是'archive'。)

    每个 ACK 响应都会通知主库关于备库的内部信息。它包含以下四个条目:

    • 已写入最新 WAL 数据的 LSN 位置
    • 已刷新最新 WAL 数据的 LSN 位置
    • 在startup进程中重放最新 WAL 数据的 LSN 位置
    • 发送此响应的时间戳

    Walreceiver 不仅在 WAL 数据写入和刷新时返回 ACK 响应,而且作为备库的心跳定期返回。因此,主库始终掌握所有连接的备库的状态。

    通过执行如下所示的查询来显示已连接备库的 LSN 相关信息。

    testdb=# SELECT application_name AS host,
                write_location AS write_LSN, 
                flush_location AS flush_LSN, 
                replay_location AS replay_LSN 
            FROM pg_stat_replication;
    
       host   | write_lsn | flush_lsn | replay_lsn 
    ----------+-----------+-----------+------------
     standby1 | 0/5000280 | 0/5000280 | 0/5000280
     standby2 | 0/5000280 | 0/5000280 | 0/5000280
    (2 rows)
    

    wal_receiver_status_interval参考控制发送心跳的时间间隔,默认为 10 秒。

    11.2.2 发送故障时的行为

    在本小节中,将描述同步备库发生故障时主库的行为以及如何处理这种情况。

    即使同步库发生故障并且不再返回ACK响应,主库也会一直等待该响应。因此,正在运行的事务无法提交,后续的查询也无法响应。换句话说,主库上的所有操作实际都停止了。(流复制不支持通过超时自动切换到异步复制模式的功能。)

    有两种方法可以避免这种情况。其中一种是使用多个备库来增加系统可用性,另一种是通过手动执行以下步骤将同步模式切换到异步模式。

    (1) 将(postgresql.conf配置文件中)参数 synchronous_standby_names 设置为空字符串

    synchronous_standby_names = ''
    

    (2) 执行带有 reload 选项的 pg_ctl 命令

    postgres> pg_ctl -D $PGDATA reload
    

    上述过程不会影响连接的客户端。主库继续进行事务处理,并保留客户端和各个后端进程之间的所有会话。

    11.3 管理多备

    在本节中,将描述多个备库流复制的工作方式。

    11.3.1 同步优先级和同步状态(sync_priority and sync_state)

    主库将sync_prioritysync_state 提供给所有托管的备库,并根据其各自的值处理每个备库。(即使主库只管理一个备库,也会提供这些值;上一节没有提到这一点)

    sync_priority 表示同步模式下备库的优先级,是一个固定值。较小的值表示较高的优先级,而0是一个特殊值,表示异步模式。备库的优先级按主库配置参数 列出的顺序。例如,在以下的配置中,standby1 和 standby2的优先级分别是1和2。

    synchronous_standby_names = 'standby1, standby2'
    

    (此参数未列出异步模式的备库,其优先级为0)

    sync_state 表示备库的状态。它根据所有备库的运行状态和各自的优先级而变化。以下是可能的状态:

    • Sync:表示所有备库(排除异步备库)中优先级最高的同步备库的运行状态
    • Potential: 所有备库(异步备库除外)中第二或更低优先级的同步备库的状态。如果该备库故障,他用优先级最高的备库替换替补备库。
    • Async:表示异步备库的状态,它是一个固定值。主库以与Potential备库相同的方式处理异步备库,只是它们的同步状态永远不会是'Sync'或'Potential'。

    可以通过发出以下查询来显示备库的优先级和状态:

    testdb=# SELECT application_name AS host, 
             sync_priority, sync_state FROM pg_stat_replication;
       host   | sync_priority | sync_state
    ----------+---------------+------------
     standby1 |             1 | sync
     standby2 |             2 | potential
    (2 rows)
    

    最近有几位开发人员正在尝试实现“多同步备库(multiple synchronous-standby)”。有关详细信息,请参见此处

    11.3.2 主库如何管理多备节点

    主库只等待来自同步备库的ACK响应。换句话说,主库只确认同步备库的WAL数据的写入和刷新。因此,流复制确保只有同步备库与主库处于一致且同步的状态。

    图 11.3 显示了(第二)候选备库的 ACK 响应比主备库(同步备库)的 ACK 响应更早返回的情况。此时,主库并不会完成当前事务的提交动作,而是继续等待主备库的 ACK 响应。然后,当收到主备库的响应时,后端进程释放latch(闩锁)并完成当前事务处理。

    Fig. 11.3. Managing multiple standby servers.

    Fig. 11.3. Managing multiple standby servers.

    standby1 和 standby2 的 (sync_state)同步状态分别为'sync'和'potential'。(1) 即使从候选备库接收到ACK响应,主库的后端进程仍需继续等待来自同步备库的ACK响应。(2) 主库的后端进程释放latch,完成当前事务处理。

    反之(即同步备库的ACK响应早于候选备库返回),主库立即完成当前事务的提交操作,而不保证候选备库是否写入和刷新WAL数据。

    11.3.3 发生故障时的行为

    再次看下备库发生故障时主库的行为。

    当候选备库或异步备库发生故障时,主库终止连接到故障备库的walsender进程并继续所有处理。换句话说,主库的事务处理不受这些任一类型备库故障的影响。

    当同步备库故障时,主库终止连接到故障备库的walsender进程,并用最高优先级的候选备库替换同步备库。见图11.4。与上一个故障相比,主库的查询处理将从故障点暂停到替换同步备库。(因此,备库的故障检测是提高复制系统可用性的一项非常重要的功能。故障检测将在下一节描述)

    Fig. 11.4. Replacing of synchronous standby server.

    Fig. 11.4. Replacing of synchronous standby server.

    在任何情况下,如果有一个或多个备库要运行在同步模式下,主库始终只保留一个同步备库,并且同步备库始终与主库处于一致和同步的状态。

    11.4 检测备用服务器的故障

    流式复制使用两种常见的故障检测程序,根本不需要任何特殊硬件。

    1. 备库进程故障检测

      当检测到 walsender 和 walreceiver 之间的连接断开时,主库立即确定备库或 walreceiver 进程出现故障。当一个低级网络函数因未能写入或读取 walreceiver 的套接字接口而返回错误时,主库也会立即确定其失败。

    2. 硬件和网络故障检测

      如果 walreceiver 在参数 wal_sender_timeout 设置的时间(默认 60 秒)内没有返回任何内容,则主库确定备库出现故障。与上述故障相比,即使备库由于某些故障(例如库的硬件故障、网络故障等)。

    根据故障的类型,通常可以在故障发生后立即检测到,但有时在故障发生和检测到故障之间可能存在时间延迟。特别是,如果同步备库发生后一种故障,则主库上的所有事务处理都将停止,直到检测到备库故障,即使多个候选备库一直在运行中。

    参数 wal_sender_timeout 在 9.2 或更早版本中称为 replication_timeout

  • 相关阅读:
    c++中stl函数的使用
    java 中String类的常见方法和StringBuffer类的使用
    c++模板类和模板函数
    c++简单工厂类的设计模式
    Android自定义的button按钮
    c++基类与派生类之间的转换
    Unity和Android结合出现Unabled to convert class into dex format
    jz2240用tftp下载程序步骤
    解决jz2440不能ping同主机问题
    android中的事件传递机制
  • 原文地址:https://www.cnblogs.com/binliubiao/p/16120211.html
Copyright © 2020-2023  润新知