掌握 PostgreSQL 复制:类型与配置详解

了解 PostgreSQL 流复制和逻辑复制的工作原理、各自适用场景,以及在生产环境故障切换前需要检查的事项。

掌握 PostgreSQL 复制:类型与设置详解

PostgreSQL 复制使备用服务器与主服务器保持高度同步,从而在硬件故障时保障业务连续性、分流读取流量或执行受控迁移。如果数据库是生产依赖,你需要在节点故障前了解哪种 PostgreSQL 复制模型符合你的风险承受能力。

PostgreSQL 提供两种常见选择:流复制和逻辑复制。流复制在物理集群级别复制 WAL。逻辑复制通过发布和订阅从选定表发送行级更改。

为什么 PostgreSQL 复制很重要

复制有助于解决四个常见的运维问题:

  • 高可用性: 如果主服务器故障,可以提升备用服务器并将应用程序指向它。
  • 灾难恢复: 位于其他位置的备用服务器可以保护你免受站点级故障的影响。
  • 读取扩展: 只读查询可以针对热备用服务器运行,而不是写入主服务器。
  • 迁移支持: 逻辑复制可以帮助在 PostgreSQL 版本或数据库布局之间移动选定的表。

复制不能替代备份。错误、糟糕的迁移或意外的 DELETE 可能会快速复制。请将经过测试的备份和即时恢复与复制一起保留。

流复制(物理复制)

流复制是 PostgreSQL 中最常见且最基本的复制形式。它通过将预写日志(WAL)记录从主服务器发送到一个或多个副本服务器来工作。这些 WAL 记录代表对数据库所做的每一次更改。副本服务器然后将这些 WAL 记录应用到自己的数据文件中,确保它们与主服务器保持一致。

同步与异步流复制

同步复制 使主服务器在向客户端报告提交之前等待一个或多个同步备用服务器。确切的安全级别取决于 synchronous_commit;例如,等待 WAL 被写入与等待它被重放是不同的。你可以获得更强的保护,防止已确认的提交丢失,但每个提交现在都依赖于副本和网络延迟。

异步复制 允许主服务器在本地提交,然后将 WAL 发送到副本服务器。写入速度更快,但主服务器崩溃可能会丢失尚未到达备用服务器的近期事务。

设置流复制(异步示例)

设置流复制涉及配置主服务器和副本服务器。以下是一个简化指南:

1. 配置主服务器(postgresql.confpg_hba.conf

在主服务器上,你需要启用 WAL 归档和复制连接。

  • postgresql.conf 修改:

    wal_level = replica  # 或 logical 用于逻辑复制
    max_wal_senders = 5  # 并发复制连接数
    wal_keep_size = 512MB # 或 wal_keep_segments 用于旧版本
    # 对于同步复制,添加:
    # synchronous_standby_names = 'replica1,replica2'
    # 或指定服务器名称/优先级:
    # synchronous_standby_names = '1 (replica1), 2 (replica2)'
    archive_mode = on
    archive_command = 'test ! -f /path/to/wal_archive/%f && cp %p /path/to/wal_archive/%f'
    
    • wal_level:对于流复制,必须至少为 replica
    • max_wal_senders:指定可以同时连接的备用服务器数量。
    • wal_keep_size:防止 WAL 文件在副本获取之前被删除(对于基本设置是 archive_command 的更简单替代方案,但建议使用归档以确保健壮性)。
    • archive_modearchive_command:对于即时恢复(PITR)以及落后时需要旧 WAL 的副本非常有用。在生产环境中,使用真实的归档目标或备份工具,而不是本地复制命令。
  • pg_hba.conf 修改:

    允许副本连接以进行复制。将 replica_ip_address 替换为副本的实际 IP。

    # TYPE  DATABASE        USER            ADDRESS                 METHOD
    host    replication     replication_user  replica_ip_address/32   md5
    

    你还需要创建一个复制用户:

    -- 在主服务器上:
    CREATE ROLE replication_user WITH REPLICATION LOGIN PASSWORD 'your_password';
    

    修改这些文件后,重新加载 PostgreSQL 配置:

    pg_ctl reload
    # 如有必要,重启 PostgreSQL
    

2. 准备副本服务器

在启动副本之前,它必须有一个数据目录,该目录是主服务器数据目录在特定时间点的副本。最简单的方法是使用 pg_basebackup

  • 停止副本上的 PostgreSQL(如果正在运行)。

  • 获取基础备份:

    # 确保 PGDATA 先为空或已删除
    pg_basebackup -h primary_host_ip -p 5432 -U replication_user -D /var/lib/postgresql/data/ -Fp -Xs -P -R
    
    • -h-p-U:指定主服务器的连接详细信息。
    • -D:副本的数据目录。
    • -Fp:格式为 plain。
    • -Xs:在备份期间流式传输 WAL。
    • -P:显示进度。
    • -R:写入备用连接设置并为 PostgreSQL 12 及更高版本创建 standby.signal
    • 系统会提示你输入 replication_user 的密码。

3. 配置副本服务器

  • postgresql.conf 修改(适用于 PG12+):

    hot_standby = on # 允许在副本上执行只读查询
    primary_conninfo = 'host=primary_host_ip port=5432 user=replication_user password=your_password'
    
    • hot_standby:在备用服务器上启用只读查询。
    • primary_conninfo:连接到主服务器的连接字符串。
  • 旧版 PostgreSQL:

    PostgreSQL 12 移除了 recovery.conf。如果你维护旧服务器,请在副本数据目录中创建 recovery.conf

    standby_mode = 'on'
    primary_conninfo = 'host=primary_host_ip port=5432 user=replication_user password=your_password'
    # 如果使用归档恢复而不是流式传输,你需要指定 restore_command
    # restore_command = 'cp /path/to/wal_archive/%f %p'
    # recovery_target_timeline = 'latest'
    

    在 PostgreSQL 12 及更高版本中,备用模式由 standby.signal 控制,而 primary_conninfo 通常位于由 pg_basebackup -R 创建的 postgresql.auto.conf 中。

4. 启动副本服务器

在副本上启动 PostgreSQL 服务。它将连接到主服务器,接收 WAL 记录,并开始同步。你可以检查日志以确认。

提示: 对于健壮的 HA,请考虑使用 Patroni 或 repmgr 等工具,它们可以自动化故障切换和管理。

逻辑复制

逻辑复制是 PostgreSQL 10 中引入的一种更灵活、更细粒度的复制形式。它不是复制整个数据块或 WAL 记录,而是根据其逻辑含义(例如 INSERT、UPDATE、DELETE 语句)在行级别复制数据更改。这是通过将 WAL 记录解码为逻辑更改流来实现的。

主要功能和用例:

  • 选择性复制: 你可以选择要复制的表。最近的 PostgreSQL 版本还支持发布中的列列表,但在依赖该功能之前请检查你的服务器版本。
  • 跨版本复制: 逻辑复制可以在不同主要版本的 PostgreSQL 之间复制数据。
  • 模式控制: 逻辑复制不会自动复制 DDL。在订阅者上创建匹配的表并应用模式迁移。
  • 数据转换: 虽然并非内置,但逻辑复制为更复杂的 ETL(提取、转换、加载)过程提供了基础。
  • 从主服务器复制到非完整克隆的副本: 目标数据库不必是源的完整物理副本。

工作原理:

  1. 发布者: 发生数据更改的源数据库(主服务器)。它需要 wal_level = logical。更改从 WAL 解码为逻辑流。
  2. 发布: 发布者上的一组命名表,其更改将被复制。
  3. 订阅者: 接收更改的目标数据库(副本)。
  4. 订阅: 订阅者上的一个连接,连接到发布者并应用来自特定发布的更改。

设置逻辑复制

1. 配置发布者(主服务器)

  • postgresql.conf 修改:

    wal_level = logical
    max_replication_slots = 10 # 用于逻辑复制槽
    max_wal_senders = 10     # 应至少为 max_replication_slots
    
  • 创建发布:

    -- 在发布者数据库上:
    CREATE PUBLICATION my_publication FOR TABLE 
        table1, 
        table2 
        WITH (publish = 'insert,update,delete');
    
    -- 或针对所有表:
    -- CREATE PUBLICATION all_tables_pub FOR ALL TABLES;
    

    在发布者上重新加载配置。

2. 配置订阅者(副本服务器)

  • 确保目标表存在: 订阅者数据库必须具有与发布者相同模式的目标表。你可以手动创建它们或使用 pg_dump 提取模式。

  • 创建订阅:

    -- 在订阅者数据库上:
    CREATE SUBSCRIPTION my_subscription
        CONNECTION 'host=publisher_host_ip port=5432 user=replication_user password=your_password dbname=publisher_db'
        PUBLICATION my_publication;
    

    replication_user 需要在发布者上具有适当的权限。

    PostgreSQL 将自动在发布者上创建复制槽并开始应用更改。你可以使用订阅者上的 pg_stat_subscription 监控订阅状态。

提示: 逻辑复制使用 PostgreSQL 内置的逻辑解码基础架构。对于基本的发布和订阅,它不需要单独的扩展。

选择正确的复制方法

  • 流复制: 适用于高可用性灾难恢复,当你需要主服务器的精确、逐字节副本时。对于完整数据库复制,设置更简单,并为只读副本提供最佳的读取可扩展性。
  • 逻辑复制: 最适合选择性数据分发迁移跨版本升级,或者当你只需要复制数据子集时。它允许更复杂的场景,例如复制到不同的模式或执行数据转换。

要点

当你需要用于故障切换、灾难恢复或只读流量的完整备用服务器时,请使用流复制。当你需要选定的表、跨版本迁移或不同数据库之间的受控数据移动时,请使用逻辑复制。

在信任任一设置之前,请运行故障切换演练、检查应用程序连接处理、监控复制延迟,并验证备份是否仍然可以正常恢复。复制使另一个服务器保持最新;它不能替代运维测试。