Skip to content

PostgreSQL 逻辑复制故障转移

概述

逻辑复制故障转移是 PostgreSQL 高可用性架构中的重要组成部分,它允许订阅者节点在发布者节点宕机时无缝切换到备用服务器,保证数据复制的连续性。

INFO

逻辑复制故障转移通过在备用服务器上同步逻辑复制槽来实现,确保在主服务器故障时订阅能够平滑过渡到新的主服务器。

核心概念

逻辑复制故障转移架构

故障转移流程

配置故障转移

启用故障转移参数

在创建订阅时,必须设置 failover = true 参数来启用故障转移功能:

sql
-- 创建启用故障转移的订阅
CREATE SUBSCRIPTION my_subscription
    CONNECTION 'host=primary_host port=5432 dbname=mydb user=replicator'
    PUBLICATION my_publication
    WITH (failover = true, copy_data = false);

参数说明:

  • failover = true:启用故障转移功能
  • copy_data = false:避免初始数据同步(如果已有数据)

配置同步备用槽

为确保故障转移成功,需要配置 synchronized_standby_slots 参数:

sql
-- 在主服务器上配置
ALTER SYSTEM SET synchronized_standby_slots = 'slot1,slot2,slot3';
SELECT pg_reload_conf();

WARNING

备用服务器必须领先于订阅者,否则故障转移时可能丢失数据。

故障转移验证步骤

第一步:识别需要同步的复制槽

在订阅者节点上执行以下查询,识别与启用故障转移的订阅关联的复制槽:

sql
-- 查询需要同步的复制槽
SELECT array_agg(quote_literal(s.subslotname)) AS slots
FROM pg_subscription s
WHERE s.subfailover AND s.subslotname IS NOT NULL;

示例输出:

 slots
-------
 {'sub1','sub2','sub3'}
(1 row)

分析过程:

  • s.subfailover:筛选启用故障转移的订阅
  • s.subslotname IS NOT NULL:确保槽名不为空
  • array_agg(quote_literal()):将结果聚合为数组并添加引号

第二步:识别表同步槽

对于每个包含启用故障转移订阅的数据库,执行以下查询:

sql
-- 查询表同步槽
SELECT array_agg(quote_literal(slot_name)) AS slots
FROM (
    SELECT CONCAT('pg_', srsubid, '_sync_', srrelid, '_', ctl.system_identifier) AS slot_name
    FROM pg_control_system() ctl,
         pg_subscription_rel r,
         pg_subscription s
    WHERE r.srsubstate = 'f'
      AND s.oid = r.srsubid
      AND s.subfailover
);

示例输出:

 slots
-------
 {'pg_16394_sync_16385_7394666715149055164'}
(1 row)

槽名组成说明: | 组成部分 | 含义 | 示例值 | |---------|------|--------| | pg_ | 固定前缀 | pg* | | srsubid | 订阅 ID | 16394 | | \_sync*| 同步标识 | sync | |srrelid| 关系ID | 16385 | |system_identifier | 系统标识符 | 7394666715149055164 |

TIP

只有表复制完成(srsubstate = 'f')的表同步槽才需要同步到备用服务器。

第三步:验证备用服务器准备就绪

在备用服务器上执行以下查询,检查所有必要的逻辑复制槽是否已准备好进行故障转移:

sql
-- 检查故障转移准备状态
SELECT slot_name,
       (synced AND NOT temporary AND NOT conflicting) AS failover_ready
FROM pg_replication_slots
WHERE slot_name IN (
    'sub1', 'sub2', 'sub3',
    'pg_16394_sync_16385_7394666715149055164'
);

示例输出:

  slot_name                                 | failover_ready
--------------------------------------------+----------------
  sub1                                      | t
  sub2                                      | t
  sub3                                      | t
  pg_16394_sync_16385_7394666715149055164   | t
(4 rows)

状态字段解释:

  • synced:槽已同步到备用服务器
  • temporary:非临时槽
  • conflicting:无冲突状态
  • failover_ready:综合判断是否准备好故障转移

实际应用场景

场景一:电商平台订单系统

某电商平台使用逻辑复制将订单数据从主数据库复制到分析数据库:

sql
-- 主数据库:创建发布
CREATE PUBLICATION orders_pub FOR TABLE orders, order_items;

-- 分析数据库:创建故障转移订阅
CREATE SUBSCRIPTION orders_analytics_sub
    CONNECTION 'host=primary.example.com port=5432 dbname=ecommerce user=replicator'
    PUBLICATION orders_pub
    WITH (failover = true, copy_data = true);

业务价值:

  • 确保分析系统的数据连续性
  • 在主数据库故障时,分析工作不中断
  • 减少业务决策延迟

场景二:多地域部署

跨地域的应用系统需要在不同地区维护数据副本:

sql
-- 配置多个订阅的故障转移
CREATE SUBSCRIPTION region_us_sub
    CONNECTION 'host=us-primary.example.com port=5432 dbname=global_app'
    PUBLICATION global_data_pub
    WITH (failover = true);

CREATE SUBSCRIPTION region_eu_sub
    CONNECTION 'host=eu-primary.example.com port=5432 dbname=global_app'
    PUBLICATION global_data_pub
    WITH (failover = true);

故障转移最佳实践

监控和告警

sql
-- 创建监控视图
CREATE VIEW replication_failover_status AS
SELECT
    slot_name,
    active,
    synced,
    temporary,
    conflicting,
    (synced AND NOT temporary AND NOT conflicting) AS ready_for_failover
FROM pg_replication_slots
WHERE slot_name LIKE '%sub%';

-- 定期检查故障转移准备状态
SELECT * FROM replication_failover_status WHERE NOT ready_for_failover;

自动化脚本示例

bash
#!/bin/bash
# 故障转移准备检查脚本

SUBSCRIBER_DB="test_sub"
STANDBY_DB="test_standby"

echo "检查订阅者节点的复制槽..."
SLOTS=$(psql -d $SUBSCRIBER_DB -t -c "
    SELECT array_to_string(array_agg(quote_literal(s.subslotname)), ',')
    FROM pg_subscription s
    WHERE s.subfailover AND s.subslotname IS NOT NULL;
")

echo "检查备用服务器故障转移准备状态..."
psql -d $STANDBY_DB -c "
    SELECT slot_name,
           (synced AND NOT temporary AND NOT conflicting) AS failover_ready
    FROM pg_replication_slots
    WHERE slot_name IN ($SLOTS);
"

故障转移执行

手动故障转移步骤

  1. 停止主服务器(如果可能)

  2. 提升备用服务器

    bash
    pg_ctl promote -D /var/lib/postgresql/data
  3. 更新订阅连接

    sql
    ALTER SUBSCRIPTION my_subscription
    CONNECTION 'host=new_primary_host port=5432 dbname=mydb user=replicator';
  4. 验证复制继续

    sql
    SELECT * FROM pg_stat_subscription;

自动故障转移

结合监控工具(如 Patroni、pg_auto_failover)实现自动故障转移:

yaml
# Patroni 配置示例
postgresql:
  parameters:
    synchronized_standby_slots: "sub1,sub2,sub3"
    wal_level: logical
    max_replication_slots: 20

故障排除

常见问题及解决方案

问题原因解决方案
槽未同步到备用服务器synchronized_standby_slots 未配置检查并配置参数
failover_ready 为 false槽存在冲突或为临时槽重新创建槽或解决冲突
故障转移后复制中断订阅连接信息未更新更新订阅的连接字符串

日志监控

sql
-- 查看复制槽状态日志
SELECT * FROM pg_stat_replication_slots WHERE slot_name LIKE '%sub%';

-- 查看订阅状态
SELECT subname, pid, received_lsn, latest_end_lsn, latest_end_time
FROM pg_stat_subscription;

性能考量

同步开销

逻辑槽同步会增加主服务器的负载:

sql
-- 监控同步延迟
SELECT
    slot_name,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flush_lsn)) AS lag_size,
    confirmed_flush_lsn
FROM pg_replication_slots
WHERE active = true;

优化建议

TIP

性能优化

  1. 合理设置同步槽数量:避免同步过多不必要的槽
  2. 监控网络带宽:确保备用服务器有足够带宽同步
  3. 定期清理:删除不再需要的复制槽
  4. 分批处理:对于大量订阅,分批启用故障转移

总结

PostgreSQL 逻辑复制故障转移为高可用性系统提供了强大的数据保护机制。通过正确配置 failover 参数、同步逻辑槽,并建立完善的监控体系,可以确保在主服务器故障时实现零停机的数据复制服务。

关键成功因素:

  • ✅ 正确配置故障转移参数
  • ✅ 确保逻辑槽及时同步
  • ✅ 建立完善的监控和告警机制
  • ✅ 制定详细的故障转移流程
  • ✅ 定期进行故障转移演练