PostgreSQL逻辑复制实战:发布订阅配置与数据同步方案

PostgreSQL逻辑复制(Logical Replication)是数据库高可用架构中的核心组件,通过解析WAL(Write-Ahead Log)日志中的逻辑变更,实现跨实例的数据同步。与物理复制不同,逻辑复制支持选择性复制表、跨大版本复制和异构架构复制,在数据迁移实战、读写分离和实时数仓同步场景中应用广泛。

逻辑复制与物理复制的区别

物理复制基于WAL字节流,从节点是主节点的完整二进制副本,不支持选择性复制,要求主从版本一致、架构一致。逻辑复制将WAL解码为逻辑变更事件(INSERT/UPDATE/DELETE),从节点可以只复制部分表,版本和架构可以不同。

特性            | 物理复制              | 逻辑复制
复制粒度        | 整个实例               | 表级别选择性复制
版本要求        | 主从版本必须一致        | 可跨大版本
架构要求        | 必须相同               | 可跨平台/架构
复制内容        | 所有数据文件+WAL       | 仅DML操作(INSERT/UPDATE/DELETE)
DDL同步        | 自动同步               | 不支持,需手动操作
从节点可写      | 否(只读)              | 是(可写)
典型用途        | 高可用容灾              | 数据同步/迁移/读写分离

发布端配置与Publication创建

逻辑复制需要将PostgreSQL的wal_level参数设置为logical,并调整相关参数确保WAL包含足够的逻辑解码信息。

-- postgresql.conf 配置
wal_level = logical
max_wal_senders = 10
max_replication_slots = 10
wal_sender_timeout = 60s

-- 重载配置(需要重启才能生效wal_level)
-- systemctl restart postgresql

-- 创建复制用户
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'Str0ngP@ss!';

-- 在发布端创建Publication
-- 复制所有表
CREATE PUBLICATION pub_all FOR ALL TABLES;

-- 复制指定表
CREATE PUBLICATION pub_ycms FOR TABLE users, articles, categories;

-- 复制指定表且仅复制特定操作
CREATE PUBLICATION pub_insert_only
  FOR TABLE logs
  WITH (publish = 'insert');

-- 查看Publication状态
SELECT * FROM pg_publication;
SELECT * FROM pg_publication_tables;

-- 向Publication添加表
ALTER PUBLICATION pub_ycms ADD TABLE comments;

-- 从Publication移除表
ALTER PUBLICATION pub_ycms DROP TABLE comments;

发布端需要确保复制的表有主键或唯一索引,逻辑复制通过主键标识行进行UPDATE和DELETE操作同步。没有主键的表只能复制INSERT操作。

-- 检查表是否有复制标识
SELECT relname, relreplident
FROM pg_class
WHERE relname IN ('users', 'articles', 'categories');

-- relreplident 值说明:
-- 'd' = 默认(主键或唯一索引)
-- 'p' = 仅主键
-- 'n' = 无标识(仅支持INSERT复制)
-- 'f' = 指定列

-- 为没有主键的表设置复制标识
ALTER TABLE logs REPLICA IDENTITY FULL;
-- FULL模式记录所有列的旧值,支持UPDATE/DELETE但日志量增大

订阅端配置与Subscription创建

订阅端需要先创建与发布端结构相同的表,然后创建Subscription连接发布端的Publication。逻辑复制不支持DDL同步,表结构变更需要手动在两端执行。

-- 订阅端创建表(结构与发布端一致)
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100) UNIQUE NOT NULL,
    email VARCHAR(255),
    created_at TIMESTAMP DEFAULT NOW()
);

-- 创建Subscription
CREATE SUBSCRIPTION sub_ycms
  CONNECTION 'host=192.168.1.10 port=5432 dbname=ycms user=replicator password=Str0ngP@ss!'
  PUBLICATION pub_ycms;

-- 带参数创建Subscription
CREATE SUBSCRIPTION sub_ycms
  CONNECTION 'host=192.168.1.10 port=5432 dbname=ycms user=replicator password=Str0ngP@ss!'
  PUBLICATION pub_ycms
  WITH (
    copy_data = true,          -- 初始同步时复制现有数据
    create_slot = true,         -- 自动创建复制槽
    slot_name = 'sub_ycms_slot',
    enabled = true,
    synchronous_commit = off    -- 异步提交提高吞吐
  );

-- 查看Subscription状态
SELECT * FROM pg_subscription;
SELECT * FROM pg_stat_subscription;

-- 监控同步进度
SELECT
    subname,
    received_lsn,
    latest_end_lsn,
    latest_end_time,
    now() - latest_end_time AS lag
FROM pg_stat_subscription;

pg_stat_subscription视图中的received_lsn和latest_end_lsn之间的差距反映了复制延迟。lag值接近0表示同步正常,lag持续增长需要排查网络延迟和从节点处理能力。

复制槽管理与WAL堆积问题处理

复制槽(Replication Slot)确保主节点在从节点确认接收前不删除WAL日志。如果从节点长时间断开,WAL会持续堆积导致磁盘写满。需要监控复制槽状态并及时处理异常。

-- 查看所有复制槽
SELECT slot_name, plugin, slot_type, active, restart_lsn
FROM pg_replication_slots;

-- 查看WAL堆积量
SELECT
    slot_name,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_size,
    active,
    active_pid
FROM pg_replication_slots;

-- 临时禁用订阅(保留复制槽和数据)
ALTER SUBSCRIPTION sub_ycms DISABLE;

-- 启用订阅
ALTER SUBSCRIPTION sub_ycms ENABLE;

-- 删除订阅(同时删除复制槽)
DROP SUBSCRIPTION sub_ycms;

-- 手动删除卡住的复制槽
SELECT pg_drop_replication_slot('sub_ycms_slot');

配置WAL保留策略和告警阈值:

-- postgresql.conf 中设置 max_slot_wal_keep_size
-- 限制单个复制槽保留的WAL最大量
max_slot_wal_keep_size = '10GB'

-- 监控脚本(定期执行)
-- 检查非活跃复制槽的WAL堆积
SELECT
    slot_name,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_size,
    active,
    CASE
        WHEN NOT active THEN 'INACTIVE - Check needed'
        ELSE 'OK'
    END AS status
FROM pg_replication_slots
WHERE NOT active
   OR pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) > 1073741824;  -- > 1GB

跨版本复制与数据迁移方案

逻辑复制支持跨大版本复制(如PG 13到PG 16),这是数据迁移实战中零停机迁移的核心方案。迁移流程为先建立逻辑复制保持数据同步,在业务低峰期切换应用连接到新版本实例。

-- 零停机迁移流程(PG 13 -> PG 16)

-- 1. 新版本PG 16实例创建与旧版本相同的表结构
-- 使用 pg_dump 导出表结构(不含数据)
pg_dump -h old-pg13 -U postgres --schema-only --no-owner ycms > schema.sql

-- 2. 在PG 16上导入表结构
psql -h new-pg16 -U postgres -d ycms < schema.sql

-- 3. 在PG 13上创建Publication
-- 需先将wal_level改为logical并重启
CREATE PUBLICATION migrate_pub FOR ALL TABLES;

-- 4. 在PG 16上创建Subscription
CREATE SUBSCRIPTION migrate_sub
  CONNECTION 'host=old-pg13 port=5432 dbname=ycms user=replicator password=Str0ngP@ss!'
  PUBLICATION migrate_pub
  WITH (copy_data = true);

-- 5. 等待初始数据同步完成
-- 监控同步状态
SELECT * FROM pg_stat_subscription WHERE subname = 'migrate_sub';
-- 确认 latest_end_lsn 不再变化且 lag 接近 0

-- 6. 验证数据一致性
-- 在两端执行行数对比
SELECT count(*) FROM users;  -- 两端结果应一致

-- 7. 业务切换(维护窗口内)
-- 应用配置改为连接新PG 16实例
-- 旧实例可降级保留作为回滚备份

-- 8. 清理
DROP SUBSCRIPTION migrate_sub;  -- 在PG 16上执行
-- 在PG 13上 DROP PUBLICATION migrate_pub;

跨版本迁移时需要注意PG版本间的默认参数变化和兼容性差异。例如PG 14+的非空列不再自动创建B树索引,PG 15+的sort性能优化可能影响查询计划。在迁移前应在新版本上运行完整测试套件验证应用兼容性。

逻辑复制冲突处理与运维最佳实践

逻辑复制冲突发生在订阅端的数据状态与发布端的变更不兼容时,常见原因包括:订阅端表被外部修改、主键冲突、约束违反。冲突发生时复制会暂停,需要手动解决后恢复。

-- 查看复制冲突日志
SELECT * FROM pg_stat_subscription WHERE subname = 'sub_ycms';

-- 常见冲突处理方式
-- 1. 主键冲突:删除订阅端冲突数据
DELETE FROM users WHERE id = 1234;  -- 删除后复制自动恢复

-- 2. 约束冲突:临时放宽约束
ALTER TABLE users ALTER COLUMN email DROP NOT NULL;
-- 复制恢复后再根据需要恢复约束

-- 3. 跳过冲突事务(PG 16+)
ALTER SUBSCRIPTION sub_ycms SKIP (lsn = '0/12345678');

运维最佳实践包括:定期检查pg_stat_subscription的复制延迟;监控复制槽的WAL堆积量;订阅端避免对复制表的直接写入(设置触发器或权限控制);在主节点配置合适的max_slot_wal_keep_size防止磁盘写满。结合pgBouncer或连接池中间件管理复制连接,避免连接数过多影响主节点性能。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/postgresql-luo-ji-fu-zhi-shi-zhan-fa-bu-ding-yue-pei-zhi-yu/

赞 (0)
小编小编
上一篇 2026年9月16日
下一篇 2026年9月16日

相关推荐

PostgreSQL逻辑复制实战:发布订阅配置与数据同步方案

PostgreSQL逻辑复制(Logical Replication)是数据库高可用架构中的核心组件,通过解析WAL(Write-Ahead Log)日志中的逻辑变更,实现跨实例的数据同步。与物理复制不同,逻辑复制支持选择性复制表、跨大版本复制和异构架构复制,在数据迁移实战、读写分离和实时数仓同步场景中应用广泛。

逻辑复制与物理复制的区别

物理复制基于WAL字节流,从节点是主节点的完整二进制副本,不支持选择性复制,要求主从版本一致、架构一致。逻辑复制将WAL解码为逻辑变更事件(INSERT/UPDATE/DELETE),从节点可以只复制部分表,版本和架构可以不同。

特性            | 物理复制              | 逻辑复制
复制粒度        | 整个实例               | 表级别选择性复制
版本要求        | 主从版本必须一致        | 可跨大版本
架构要求        | 必须相同               | 可跨平台/架构
复制内容        | 所有数据文件+WAL       | 仅DML操作(INSERT/UPDATE/DELETE)
DDL同步        | 自动同步               | 不支持,需手动操作
从节点可写      | 否(只读)              | 是(可写)
典型用途        | 高可用容灾              | 数据同步/迁移/读写分离

发布端配置与Publication创建

逻辑复制需要将PostgreSQL的wal_level参数设置为logical,并调整相关参数确保WAL包含足够的逻辑解码信息。

-- postgresql.conf 配置
wal_level = logical
max_wal_senders = 10
max_replication_slots = 10
wal_sender_timeout = 60s

-- 重载配置(需要重启才能生效wal_level)
-- systemctl restart postgresql

-- 创建复制用户
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'Str0ngP@ss!';

-- 在发布端创建Publication
-- 复制所有表
CREATE PUBLICATION pub_all FOR ALL TABLES;

-- 复制指定表
CREATE PUBLICATION pub_ycms FOR TABLE users, articles, categories;

-- 复制指定表且仅复制特定操作
CREATE PUBLICATION pub_insert_only
  FOR TABLE logs
  WITH (publish = 'insert');

-- 查看Publication状态
SELECT * FROM pg_publication;
SELECT * FROM pg_publication_tables;

-- 向Publication添加表
ALTER PUBLICATION pub_ycms ADD TABLE comments;

-- 从Publication移除表
ALTER PUBLICATION pub_ycms DROP TABLE comments;

发布端需要确保复制的表有主键或唯一索引,逻辑复制通过主键标识行进行UPDATE和DELETE操作同步。没有主键的表只能复制INSERT操作。

-- 检查表是否有复制标识
SELECT relname, relreplident
FROM pg_class
WHERE relname IN ('users', 'articles', 'categories');

-- relreplident 值说明:
-- 'd' = 默认(主键或唯一索引)
-- 'p' = 仅主键
-- 'n' = 无标识(仅支持INSERT复制)
-- 'f' = 指定列

-- 为没有主键的表设置复制标识
ALTER TABLE logs REPLICA IDENTITY FULL;
-- FULL模式记录所有列的旧值,支持UPDATE/DELETE但日志量增大

订阅端配置与Subscription创建

订阅端需要先创建与发布端结构相同的表,然后创建Subscription连接发布端的Publication。逻辑复制不支持DDL同步,表结构变更需要手动在两端执行。

-- 订阅端创建表(结构与发布端一致)
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100) UNIQUE NOT NULL,
    email VARCHAR(255),
    created_at TIMESTAMP DEFAULT NOW()
);

-- 创建Subscription
CREATE SUBSCRIPTION sub_ycms
  CONNECTION 'host=192.168.1.10 port=5432 dbname=ycms user=replicator password=Str0ngP@ss!'
  PUBLICATION pub_ycms;

-- 带参数创建Subscription
CREATE SUBSCRIPTION sub_ycms
  CONNECTION 'host=192.168.1.10 port=5432 dbname=ycms user=replicator password=Str0ngP@ss!'
  PUBLICATION pub_ycms
  WITH (
    copy_data = true,          -- 初始同步时复制现有数据
    create_slot = true,         -- 自动创建复制槽
    slot_name = 'sub_ycms_slot',
    enabled = true,
    synchronous_commit = off    -- 异步提交提高吞吐
  );

-- 查看Subscription状态
SELECT * FROM pg_subscription;
SELECT * FROM pg_stat_subscription;

-- 监控同步进度
SELECT
    subname,
    received_lsn,
    latest_end_lsn,
    latest_end_time,
    now() - latest_end_time AS lag
FROM pg_stat_subscription;

pg_stat_subscription视图中的received_lsn和latest_end_lsn之间的差距反映了复制延迟。lag值接近0表示同步正常,lag持续增长需要排查网络延迟和从节点处理能力。

复制槽管理与WAL堆积问题处理

复制槽(Replication Slot)确保主节点在从节点确认接收前不删除WAL日志。如果从节点长时间断开,WAL会持续堆积导致磁盘写满。需要监控复制槽状态并及时处理异常。

-- 查看所有复制槽
SELECT slot_name, plugin, slot_type, active, restart_lsn
FROM pg_replication_slots;

-- 查看WAL堆积量
SELECT
    slot_name,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_size,
    active,
    active_pid
FROM pg_replication_slots;

-- 临时禁用订阅(保留复制槽和数据)
ALTER SUBSCRIPTION sub_ycms DISABLE;

-- 启用订阅
ALTER SUBSCRIPTION sub_ycms ENABLE;

-- 删除订阅(同时删除复制槽)
DROP SUBSCRIPTION sub_ycms;

-- 手动删除卡住的复制槽
SELECT pg_drop_replication_slot('sub_ycms_slot');

配置WAL保留策略和告警阈值:

-- postgresql.conf 中设置 max_slot_wal_keep_size
-- 限制单个复制槽保留的WAL最大量
max_slot_wal_keep_size = '10GB'

-- 监控脚本(定期执行)
-- 检查非活跃复制槽的WAL堆积
SELECT
    slot_name,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_size,
    active,
    CASE
        WHEN NOT active THEN 'INACTIVE - Check needed'
        ELSE 'OK'
    END AS status
FROM pg_replication_slots
WHERE NOT active
   OR pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) > 1073741824;  -- > 1GB

跨版本复制与数据迁移方案

逻辑复制支持跨大版本复制(如PG 13到PG 16),这是数据迁移实战中零停机迁移的核心方案。迁移流程为先建立逻辑复制保持数据同步,在业务低峰期切换应用连接到新版本实例。

-- 零停机迁移流程(PG 13 -> PG 16)

-- 1. 新版本PG 16实例创建与旧版本相同的表结构
-- 使用 pg_dump 导出表结构(不含数据)
pg_dump -h old-pg13 -U postgres --schema-only --no-owner ycms > schema.sql

-- 2. 在PG 16上导入表结构
psql -h new-pg16 -U postgres -d ycms < schema.sql

-- 3. 在PG 13上创建Publication
-- 需先将wal_level改为logical并重启
CREATE PUBLICATION migrate_pub FOR ALL TABLES;

-- 4. 在PG 16上创建Subscription
CREATE SUBSCRIPTION migrate_sub
  CONNECTION 'host=old-pg13 port=5432 dbname=ycms user=replicator password=Str0ngP@ss!'
  PUBLICATION migrate_pub
  WITH (copy_data = true);

-- 5. 等待初始数据同步完成
-- 监控同步状态
SELECT * FROM pg_stat_subscription WHERE subname = 'migrate_sub';
-- 确认 latest_end_lsn 不再变化且 lag 接近 0

-- 6. 验证数据一致性
-- 在两端执行行数对比
SELECT count(*) FROM users;  -- 两端结果应一致

-- 7. 业务切换(维护窗口内)
-- 应用配置改为连接新PG 16实例
-- 旧实例可降级保留作为回滚备份

-- 8. 清理
DROP SUBSCRIPTION migrate_sub;  -- 在PG 16上执行
-- 在PG 13上 DROP PUBLICATION migrate_pub;

跨版本迁移时需要注意PG版本间的默认参数变化和兼容性差异。例如PG 14+的非空列不再自动创建B树索引,PG 15+的sort性能优化可能影响查询计划。在迁移前应在新版本上运行完整测试套件验证应用兼容性。

逻辑复制冲突处理与运维最佳实践

逻辑复制冲突发生在订阅端的数据状态与发布端的变更不兼容时,常见原因包括:订阅端表被外部修改、主键冲突、约束违反。冲突发生时复制会暂停,需要手动解决后恢复。

-- 查看复制冲突日志
SELECT * FROM pg_stat_subscription WHERE subname = 'sub_ycms';

-- 常见冲突处理方式
-- 1. 主键冲突:删除订阅端冲突数据
DELETE FROM users WHERE id = 1234;  -- 删除后复制自动恢复

-- 2. 约束冲突:临时放宽约束
ALTER TABLE users ALTER COLUMN email DROP NOT NULL;
-- 复制恢复后再根据需要恢复约束

-- 3. 跳过冲突事务(PG 16+)
ALTER SUBSCRIPTION sub_ycms SKIP (lsn = '0/12345678');

运维最佳实践包括:定期检查pg_stat_subscription的复制延迟;监控复制槽的WAL堆积量;订阅端避免对复制表的直接写入(设置触发器或权限控制);在主节点配置合适的max_slot_wal_keep_size防止磁盘写满。结合pgBouncer或连接池中间件管理复制连接,避免连接数过多影响主节点性能。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/postgresql-luo-ji-fu-zhi-shi-zhan-fa-bu-ding-yue-pei-zhi-yu/

赞 (0)
小编小编
上一篇 2026年9月11日
下一篇 2026年9月11日

相关推荐

PostgreSQL逻辑复制实战:发布订阅配置与多数据中心双向同步方案

PostgreSQL逻辑复制基于WAL日志解析实现表级数据同步,相比物理复制具备选择性复制、跨版本兼容、双向同步等优势。在多数据中心高可用架构、读写分离、在线数据迁移等场景中应用广泛。本文从发布订阅基础配置、冲突处理机制、多活双向同步三个维度给出完整配置方案。

逻辑复制核心概念与前置配置

逻辑复制分为Publisher(发布端)和Subscriber(订阅端)。发布端创建Publication指定要复制的表,订阅端创建Subscription连接发布端并接收变更。逻辑复制基于WAL日志的logical decoding机制,需要配置wal_level=logical。

-- 发布端 postgresql.conf 配置
-- wal_level = logical
-- max_replication_slots = 10
-- max_wal_senders = 10
-- max_worker_processes = 15

-- 重启PostgreSQL后生效
ALTER SYSTEM SET wal_level = 'logical';
ALTER SYSTEM SET max_replication_slots = 10;
ALTER SYSTEM SET max_wal_senders = 10;
SELECT pg_reload_conf();

-- 创建复制专用用户
CREATE ROLE replicator REPLICATION LOGIN PASSWORD 'Str0ngPass!2026';
GRANT CONNECT ON DATABASE appdb TO replicator;

-- pg_hba.conf 添加复制权限
-- host    replication    replicator    10.0.0.0/8    md5
-- host    appdb          replicator    10.0.0.0/8    md5

-- 重新加载pg_hba.conf
SELECT pg_reload_conf();

max_replication_slots设置逻辑复制槽数量,每个Subscription对应一个复制槽。max_wal_senders设置WAL发送进程上限,需大于逻辑和物理复制的总连接数。复制槽会在订阅端断连后保留WAL日志,防止数据丢失,但长期断连会导致WAL堆积。

Publication发布配置

-- 发布端:创建Publication
-- 方式1:发布所有表
CREATE PUBLICATION pub_appdb FOR ALL TABLES;

-- 方式2:指定表和操作类型(推荐)
CREATE PUBLICATION pub_appdb
  FOR TABLE users, orders, order_items, products
  WITH (publish = 'insert, update, delete, truncate');

-- 查看Publication状态
SELECT * FROM pg_publication;
SELECT * FROM pg_publication_tables WHERE pubname = 'pub_appdb';

-- 动态添加/移除表
ALTER PUBLICATION pub_appdb ADD TABLE categories;
ALTER PUBLICATION pub_appdb DROP TABLE products;

-- 修改发布操作类型
ALTER PUBLICATION pub_appdb SET (publish = 'insert, update');

publish参数控制哪些DML操作被复制。仅同步增量数据时设为insert, update即可;需要完整同步则包含delete和truncate。选择性发布表可以减少网络传输量,避免将日志表、临时表等不需要同步的表纳入复制。

-- 发布端:确保被复制的表有REPLICA IDENTITY
-- 默认值为default(使用主键)
-- 无主键表需设置为full(使用所有列作为标识)
ALTER TABLE products REPLICA IDENTITY FULL;
ALTER TABLE order_items REPLICA IDENTITY USING INDEX idx_order_items_unique;

-- 查看表REPLICA IDENTITY设置
SELECT relname, relreplident FROM pg_class
  WHERE relname IN ('users', 'orders', 'products', 'order_items');

REPLICA IDENTITY决定UPDATE和DELETE操作在WAL日志中记录哪些列作为旧值标识。default模式仅记录主键,订阅端通过主键定位行执行更新。无主键表设为full会记录所有列旧值,增加WAL日志量但保证复制正确性。

Subscription订阅配置

-- 订阅端:创建Subscription
-- 前提:订阅端已存在相同结构的表
CREATE SUBSCRIPTION sub_appdb
  CONNECTION 'host=10.0.1.10 port=5432 dbname=appdb user=replicator password=Str0ngPass!2026'
  PUBLICATION pub_appdb
  WITH (
    copy_data = true,          -- 初始数据同步
    create_slot = true,        -- 自动创建复制槽
    slot_name = 'sub_appdb_slot',
    enabled = true,
    synchronous_commit = off   -- 异步提交降低延迟
  );

-- 查看Subscription状态
SELECT * FROM pg_subscription;
SELECT * FROM pg_stat_subscription;

-- 查看复制进度
SELECT * FROM pg_replication_slots WHERE slot_name = 'sub_appdb_slot';

copy_data=true在首次创建订阅时复制现有数据快照,后续仅同步增量变更。synchronous_commit=off设置异步提交,减少WAL落盘延迟提升复制吞吐量,代价是崩溃时可能丢失极少量事务。create_slot=true自动在发布端创建逻辑复制槽。

数据冲突检测与处理

逻辑复制在双向同步或数据不一致时可能产生冲突。PostgreSQL不内置自动冲突解决机制,冲突发生时订阅端会停止复制并记录错误日志。

-- 查看订阅错误日志
SELECT * FROM pg_stat_subscription
  WHERE subname = 'sub_appdb';

-- 常见冲突类型:
-- 1. 主键冲突:订阅端已存在相同主键的行
-- 2. 外键冲突:引用的外键行不存在
-- 3. 唯一约束冲突:唯一索引列值重复

-- 处理主键冲突:跳过冲突事务
-- 方式1:修改订阅跳过冲突(PG 16+)
ALTER SUBSCRIPTION sub_appdb SET (skip_lsn = '0/1A2B3C4D');

-- 方式2:暂停订阅,手动修复后恢复
ALTER SUBSCRIPTION sub_appdb DISABLE;
-- 手动修复冲突数据...
ALTER SUBSCRIPTION sub_appdb ENABLE;

-- 方式3:刷新数据重新同步单表
ALTER SUBSCRIPTION sub_appdb REFRESH PUBLICATION
  WITH (copy_data = true);

skip_lsn参数跳过指定LSN位置的事务,适用于偶发冲突。DISABLE/ENABLE方式暂停后手动修复冲突行再恢复,适合需要数据干预的场景。REFRESH PUBLICATION重新同步表结构和初始数据,适合表结构变更后的全量刷新。

多数据中心双向同步配置

双向同步需要两个节点互为发布者和订阅者,关键配置是避免数据循环复制。PostgreSQL通过origin机制标记数据来源,相同origin的变更不会被反向复制。

-- 节点A (10.0.1.10) 配置
-- 创建Publication
CREATE PUBLICATION pub_node_a
  FOR TABLE users, orders, order_items
  WITH (publish = 'insert, update, delete, truncate');

-- 创建到节点B的Subscription
CREATE SUBSCRIPTION sub_from_node_b
  CONNECTION 'host=10.0.2.10 port=5432 dbname=appdb user=replicator password=Str0ngPass!2026'
  PUBLICATION pub_node_b
  WITH (
    copy_data = false,         -- 双向同步不做初始拷贝
    create_slot = true,
    enabled = true,
    origin = 'node_a'           -- 标记origin防止循环
  );

-- 节点B (10.0.2.10) 配置
CREATE PUBLICATION pub_node_b
  FOR TABLE users, orders, order_items
  WITH (publish = 'insert, update, delete, truncate');

CREATE SUBSCRIPTION sub_from_node_a
  CONNECTION 'host=10.0.1.10 port=5432 dbname=appdb user=replicator password=Str0ngPass!2026'
  PUBLICATION pub_node_a
  WITH (
    copy_data = false,
    create_slot = true,
    enabled = true,
    origin = 'node_b'
  );

origin参数是双向同步防循环的核心机制。节点A产生的数据origin标记为node_a,节点B收到后不会反向复制回节点A,因为sub_from_node_a的origin为node_a会被自动跳过。copy_data=false避免双向同步时重复复制初始数据。多活场景中建议每条记录增加节点标识列,避免不同节点同时写入导致主键冲突。

-- 多活冲突预防:使用序列分配不同ID范围
-- 节点A:序列从1开始步长2(奇数)
CREATE SEQUENCE users_id_seq START 1 INCREMENT 2;
-- 节点B:序列从2开始步长2(偶数)
CREATE SEQUENCE users_id_seq START 2 INCREMENT 2;

-- 或使用UUID主键避免序列冲突
ALTER TABLE users ADD COLUMN id UUID DEFAULT gen_random_uuid() PRIMARY KEY;

双向同步场景下,自增主键必然冲突。序列奇偶分配方案简单但节点扩展受限,UUID主键方案天然避免冲突但牺牲顺序性。业务层写入时通过负载均衡分发到不同节点,减少同一记录同时被修改的概率。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/postgresql-luo-ji-fu-zhi-shi-zhan-fa-bu-ding-yue-pei-zhi-yu/

赞 (0)
小编小编
上一篇 2026年8月25日
下一篇 2026年8月25日

相关推荐