ITPub博客

首页 > 数据库 > PostgreSQL > 数据库教程FGMT63‑PostgreSQL逻辑复制技术与项目实战

数据库教程FGMT63‑PostgreSQL逻辑复制技术与项目实战

原创 PostgreSQL 作者:mfkqwyc86 时间:2026-10-21 11:19:09 0 删除 编辑

数据库教程FGMT63‑PostgreSQL逻辑复制技术与项目实战


## 前言与内容大纲介绍


PostgreSQL 逻辑复制是数据库架构设计、数据迁移、高可用建设与业务系统改造中的关键能力之一。与传统物理复制不同,逻辑复制基于数据行变化和逻辑解码机制,可以实现更细粒度的数据订阅、跨版本数据同步、部分表迁移、双向或多源汇聚等场景。本套风哥教程面向具备 PostgreSQL 基础运维能力的读者,围绕逻辑复制的理论模型、内置配置、项目环境准备、单表与多表复制、监控运维、pglogical 扩展以及综合项目实战展开。


风哥教程本文的目标不是简单罗列命令,而是帮助读者建立从原理到生产落地的完整路径。文中所有实战环境均按 64G 内存、8CPU 的服务器规格进行规划,主机名统一使用 `fgedu-net-cn1` 和 `fgedu-net-cn2`,数据库名、实例名和用户名统一使用 `fgedudb`,路径由常规 `/u01` 调整为 `/fgedudb`,便于形成统一的实验与交付模板。


本套风哥教程内容结构如下:


1. 逻辑复制基础与理论模型  

2. PostgreSQL 逻辑复制核心架构  

3. 发布、订阅与复制槽机制  

4. 项目环境准备与系统参数规划  

5. 单表逻辑复制实战配置  

6. 多表逻辑复制实战配置  

7. 逻辑复制运维与监控  

8. pglogical 扩展配置实战  

9. 综合实战一:同步模式、表结构与表数据  

10. 综合实战二:多对一数据汇聚  

11. 风哥针对本文总结  


---


# 1. 逻辑复制基础与理论模型


## 1.1 什么是逻辑复制


逻辑复制是指数据库通过解析 WAL 日志中的逻辑变更记录,将数据行级别的 INSERT、UPDATE、DELETE 等操作发送给一个或多个订阅端,订阅端再按照事务语义将这些变更应用到本地数据库。与物理复制复制整个磁盘页或块不同,逻辑复制复制的是“数据变化的逻辑内容”。


这意味着逻辑复制可以做到:


- 只复制指定表,而不是整个实例;

- 对复制内容进行过滤;

- 在订阅端进行不同的数据处理;

- 支持跨大版本迁移中的数据同步;

- 实现多源数据汇聚;

- 为数据迁移、灰度发布和业务改造提供在线同步能力。


## 1.2 逻辑复制的适用场景


### 1.2.1 数据迁移与版本升级


当业务需要将数据库从旧环境迁移到新环境,或进行大版本升级时,逻辑复制可以作为在线迁移工具的一部分。源端持续发布增量变更,目标端先同步历史数据,再追赶增量,最终在业务低峰期完成切换。


### 1.2.2 部分业务数据同步


并不是所有业务都需要全库复制。很多场景只需要同步核心业务表、配置表、字典表或审计表。逻辑复制通过发布对象列表控制复制范围,非常适合这类需求。


### 1.2.3 多源数据汇聚


在报表系统、数据中台或分库分表改造场景中,多个业务库可能需要将数据汇聚到一个目标库中进行统一分析。逻辑复制可以支持多源到单目标的数据汇聚架构。


### 1.2.4 读写分离与查询卸载


虽然逻辑复制不是传统意义上的流复制高可用方案,但在一些允许延迟的业务中,可以将分析查询、报表查询和后台统计任务导向订阅端,从而降低主库压力。


## 1.3 逻辑复制的限制与边界


逻辑复制虽然灵活,但也有明确边界。它不复制 DDL 定义,不复制大对象,不复制序列,不复制用户权限,也不保证源端和订阅端在结构上完全一致。因此,在项目设计阶段必须明确:哪些对象由逻辑复制负责,哪些对象需要额外通过 DDL 脚本、备份恢复或应用迁移完成。


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


物理复制关注磁盘数据块的一致性,逻辑复制关注数据行变化的逻辑一致性。物理复制通常要求主备版本严格一致,复制粒度粗,但一致性强;逻辑复制粒度细、拓扑灵活,但需要更多结构对齐和应用层处理。


在项目选型时,如果目标是高可用和强一致灾备,物理复制更合适;如果目标是数据迁移、部分同步、多源汇聚或跨版本迁移,逻辑复制更合适。


---


# 2. PostgreSQL 逻辑复制核心架构


## 2.1 逻辑复制的主要组件


PostgreSQL 逻辑复制由几个核心组件协同完成:


- **发布端**:产生数据变更并对外提供变更流;

- **订阅端**:拉取并应用变更;

- **发布对象**:被选中复制的表或表组;

- **复制槽**:记录发布端已经发送到订阅端的位置;

- **逻辑解码插件**:将 WAL 中的二进制日志解析为可读的逻辑变更格式;

- **复制协议**:发布端与订阅端之间的数据传输通道。


## 2.2 发布端工作机制


发布端的核心任务是捕获数据变化并将其发送给订阅端。当用户在发布端执行 INSERT、UPDATE、DELETE 时,变化会先写入 WAL。逻辑复制服务随后通过逻辑解码机制,将这些变化转换为逻辑变更消息,并根据发布对象进行过滤。


发布端不需要提前知道所有订阅端的存在,订阅端通过复制槽来维护自己的消费位置。发布端只需要保证,在订阅端尚未消费之前,相关 WAL 不会被过早回收。


## 2.3 订阅端工作机制


订阅端通过后台进程连接发布端,从指定复制槽开始拉取增量变更。拉取到的变更会先进入本地临时存储,然后由应用进程按照事务顺序应用到订阅端表中。


订阅端应用变更时,会使用发布端发送的主键或 replica identity 来定位目标行。如果订阅端表缺少有效 replica identity,UPDATE 和 DELETE 操作可能无法正确应用。


## 2.4 复制槽的作用


复制槽是逻辑复制中非常重要的概念。它用于记录订阅端已经消费到的 WAL 位置,即使订阅端临时断开,发布端也知道从哪里继续发送。


复制槽的存在会阻止 PostgreSQL 回收尚未被消费的 WAL。因此,如果订阅端长期断开,发布端的 WAL 可能会持续堆积,最终导致磁盘空间增长。生产环境中必须对复制槽状态和 WAL 积压进行监控。


## 2.5 事务顺序与一致性


逻辑复制通常按照发布端事务提交顺序进行应用。这意味着订阅端可以获得与发布端一致的事务视图。但需要注意,逻辑复制并不保证“实时强一致”,它存在复制延迟,尤其在网络抖动、发布端高负载或订阅端应用压力较大时,延迟可能明显增加。


---


# 3. 发布、订阅与复制槽机制


## 3.1 发布的概念


发布是发布端上定义的一个复制出口。一个发布可以包含一个或多个表,也可以定义为复制所有表。发布本身不决定谁来消费,它只决定哪些数据可以被复制。


在生产项目中,建议按业务模块创建发布,而不是把所有表都放在一个DF布中。这样可以降低故障影响范围,也便于后续按业务线进行维护。


## 3.2 订阅的概念


订阅是订阅端上定义的一个复制入口。订阅会指定发布端连接信息、发布名称、复制槽名称以及目标表映射关系。订阅创建后,PostgreSQL 会自动建立复制连接并开始拉取增量。


## 3.3 复制槽与订阅生命周期


复制槽与订阅之间存在紧密关系。订阅在创建时通常会指定复制槽,复制槽记录订阅端的接收位置。如果订阅被删除,复制槽是否保留取决于配置,生产环境中需要谨慎处理,避免出现孤立复制槽。


## 3.4 数据变更过滤


逻辑复制支持基于发布对象的过滤。例如,只发布某些表,或在表级别指定列。这样可以避免把不需要的测试表、日志表或临时表同步到目标端。


## 3.5 复制方向与拓扑


逻辑复制常见拓扑包括:


- 一对一单向复制;

- 一对多分发;

- 多对一汇聚;

- 双向复制。


其中,双向复制最复杂,必须处理冲突、循环复制、序列同步和外键依赖问题。没有成熟的冲突处理方案,不建议在核心业务中盲目使用双向逻辑复制。


---


# 4. 项目环境准备与系统参数规划


## 4.1 环境规格说明


本套风哥教程中的所有实战均按 64G 内存、8CPU 服务器规格设计。实际配置时,应根据业务负载、并发连接数、WAL 生成速率和复制延迟目标进行微调。


环境规划如下:


| 项目 | 规划值 |

|---|---|

| 主机名 | `fgedu-net-cn1`、`fgedu-net-cn2` |

| 数据库名 | `fgedudb` |

| 实例名 | `fgedudb` |

| 用户名 | `fgedu` |

| 数据目录 | `/fgedudb` |

| 架构 | 发布端 `fgedu-net-cn1`,订阅端 `fgedu-net-cn2` |


## 4.2 系统内核参数规划


针对 64G 内存、8CPU 环境,建议调整以下内核参数。这些参数会影响共享内存、网络连接、文件句柄和进程调度能力。


```bash

cat >> /etc/sysctl.conf << 'EOF'

fs.file-max = 655360

kernel.shmall = 4294967296

kernel.shmmax = 34359738368

kernel.shmmni = 4096

kernel.sem = 250 32000 32 128

net.core.somaxconn = 65535

net.core.rmem_default = 262144

net.core.wmem_default = 262144

net.core.rmem_max = 16777216

net.core.wmem_max = 16777216

vm.dirty_ratio = 15

vm.dirty_background_ratio = 5

EOF


sysctl -p

```


说明:`shmmax` 设置为 34GB 左右,是为了满足 PostgreSQL 共享内存段的分配需求;`somaxconn` 用于提升高并发连接能力;`dirty_ratio` 系列参数用于控制写回磁盘的节奏。


## 4.3 用户资源限制规划


```bash

cat >> /etc/security/limits.conf << 'EOF'

fgedu soft nofile 65536

fgedu hard nofile 65536

fgedu soft nproc 65536

fgedu hard nproc 65536

fgedu soft memlock unlimited

fgedu hard memlock unlimited

EOF

```


## 4.4 目录规划


```bash

mkdir -p /fgedudb/pgdata

mkdir -p /fgedudb/pg_wal

mkdir -p /fgedudb/pg_log

mkdir -p /fgedudb/backup

chown -R fgedu:fgedu /fgedudb

chmod 700 /fgedudb/pgdata

```


## 4.5 数据库参数规划


针对 64G 内存、8CPU 环境,建议在 `postgresql.conf` 中配置以下参数:


```ini

max_connections = 400

shared_buffers = 16GB

effective_cache_size = 48GB

maintenance_work_mem = 4GB

work_mem = 64MB

wal_buffers = 512MB

min_wal_size = 4GB

max_wal_size = 16GB

checkpoint_timeout = 30min

checkpoint_completion_target = 0.9

bgwriter_delay = 10ms

bgwriter_lru_maxpages = 512

max_parallel_workers_per_gather = 4

max_parallel_maintenance_workers = 4

max_worker_processes = 16

wal_level = logical

archive_mode = on

archive_command = 'cp %p /fgedudb/backup/archive/%f'

max_replication_slots = 10

max_logical_replication_workers = 8

max_sync_workers_per_subscription = 4

```


参数说明:


- `wal_level = logical`:开启逻辑复制所需的 WAL 级别;

- `shared_buffers = 16GB`:约为内存的 25%;

- `effective_cache_size = 48GB`:约为内存的 75%;

- `max_worker_processes = 16`:支持更多后台复制进程;

- `max_logical_replication_workers`:控制逻辑复制工作进程数量;

- `max_sync_workers_per_subscription`:控制每个订阅的同步并行度。


## 4.6 pg_hba.conf 配置


发布端和订阅端之间需要建立复制连接,因此必须在 `pg_hba.conf` 中允许复制链路。


```ini

host    fgedudb         fgedu           fgedu-net-cn2/32        scram-sha-256

host    replication     fgedu           fgedu-net-cn2/32        scram-sha-256

```


配置完成后重载配置:


```sql

SELECT pg_reload_conf();

```


## 4.7 主机名解析配置


为避免依赖 DNS,可在两台主机的 `/etc/hosts` 中配置主机名解析。


```bash

cat >> /etc/hosts << 'EOF'

192.168.10.11 fgedu-net-cn1

192.168.10.12 fgedu-net-cn2

EOF

```


## 4.8 数据库初始化


在 `fgedu-net-cn1` 上初始化数据库:


```bash

su - fgedu

initdb -D /fgedudb/pgdata -U fgedu -W

```


在 `fgedu-net-cn2` 上执行相同操作。初始化完成后,修改 `postgresql.conf` 和 `pg_hba.conf`,然后启动数据库。


```bash

pg_ctl start -D /fgedudb/pgdata

```


## 4.9 创建业务数据库与用户


```sql

CREATE DATABASE fgedudb OWNER fgedu;

CREATE USER fgedu WITH PASSWORD 'Fgedu@2025';

ALTER USER fgedu WITH SUPERUSER;

GRANT ALL PRIVILEGES ON DATABASE fgedudb TO fgedu;

```


说明:项目实战阶段为便于配置,可临时授予 `fgedu` 较高权限;生产环境应按最小权限原则逐步收敛。


---


# 5. 单表逻辑复制实战配置


## 5.1 实战目标


在 `fgedu-net-cn1` 上创建一张业务表 `t_order`,并将其通过逻辑复制同步到 `fgedu-net-cn2`。这是逻辑复制最基础、最常用的项目模板。


## 5.2 在发布端创建测试表


```sql

\c fgedudb fgedu


CREATE TABLE t_order (

    order_id BIGINT PRIMARY KEY,

    user_id BIGINT NOT NULL,

    amount NUMERIC(12,2) NOT NULL DEFAULT 0,

    status SMALLINT NOT NULL DEFAULT 0,

    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);


CREATE INDEX idx_t_order_user_id ON t_order(user_id);

CREATE INDEX idx_t_order_create_time ON t_order(create_time);

```


## 5.3 插入初始数据


```sql

INSERT INTO t_order (order_id, user_id, amount, status)

SELECT i, i%1000, ROUND((i*13.7)::numeric,2), i%5

FROM generate_series(1,100000) i;


ANALYZE t_order;

```


## 5.4 在发布端创建发布


```sql

CREATE PUBLICATION pub_t_order FOR TABLE t_order;

```


查看发布信息:


```sql

SELECT pubname, puballtables, pubinsert, pubupdate, pubdelete

FROM pg_publication;

```


## 5.5 在订阅端创建目标表


订阅端表结构应与发布端保持一致。为确保结构一致,建议先通过备份或 DDL 脚本创建。


```sql

\c fgedudb fgedu


CREATE TABLE t_order (

    order_id BIGINT PRIMARY KEY,

    user_id BIGINT NOT NULL,

    amount NUMERIC(12,2) NOT NULL DEFAULT 0,

    status SMALLINT NOT NULL DEFAULT 0,

    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);

```


## 5.6 在订阅端创建订阅


```sql

CREATE SUBSCRIPTION sub_t_order

CONNECTION 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

PUBLICATION pub_t_order;

```


创建订阅时,PostgreSQL 会自动创建复制槽并开始同步数据。如果目标表中已有数据,可能需要使用 `copy_data = false`,然后手动完成初始数据同步。


## 5.7 查看订阅状态


```sql

SELECT subname, subenabled, subconninfo, subpublications

FROM pg_subscription;

```


## 5.8 查看复制槽状态


在发布端执行:


```sql

SELECT slot_name, slot_type, active, restart_lsn, confirmed_flush_lsn

FROM pg_replication_slots;

```


## 5.9 验证数据同步


在发布端插入新数据:


```sql

INSERT INTO t_order (order_id, user_id, amount, status)

VALUES (999999, 8888, 1288.50, 1);

```


在订阅端查询:


```sql

SELECT * FROM t_order WHERE order_id = 999999;

```


如果订阅端能够查到该记录,说明单表逻辑复制链路已经建立成功。


## 5.10 增量更新测试


在发布端执行更新:


```sql

UPDATE t_order SET status = 2, update_time = CURRENT_TIMESTAMP

WHERE order_id = 999999;

```


订阅端验证:


```sql

SELECT order_id, status, update_time FROM t_order WHERE order_id = 999999;

```


## 5.11 删除操作测试


发布端:


```sql

DELETE FROM t_order WHERE order_id = 999999;

```


订阅端:


```sql

SELECT count(*) FROM t_order WHERE order_id = 999999;

```


## 5.12 单表复制常见问题


### 5.12.1 表没有主键导致复制失败


如果表没有主键或 replica identity,UPDATE 和 DELETE 可能无法正确应用。建议业务表都设计主键。


```sql

ALTER TABLE t_order REPLICA IDENTITY USING INDEX t_order_pkey;

```


### 5.12.2 初始数据已经存在导致冲突


如果订阅端已有数据,创建订阅时可能出现主键冲突。此时应先清空订阅端数据,或使用 `copy_data = false`。


```sql

CREATE SUBSCRIPTION sub_t_order

CONNECTION 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

PUBLICATION pub_t_order

WITH (copy_data = false);

```


### 5.12.3 复制延迟较高


延迟较高通常与发布端 WAL 生成速度、订阅端应用速度、网络带宽和磁盘 IO 有关。应结合复制延迟指标和系统负载进行排查。


---


# 6. 多表逻辑复制实战配置


## 6.1 实战目标


在项目中,很多场景需要同步一组业务表,而不是单张表。本套风哥教程本文演示如何将订单表、用户表和支付表组成一个业务模块,统一发布到订阅端。


## 6.2 在发布端创建多张业务表


```sql

\c fgedudb fgedu


CREATE TABLE t_user (

    user_id BIGINT PRIMARY KEY,

    username VARCHAR(128) NOT NULL,

    mobile VARCHAR(32),

    status SMALLINT NOT NULL DEFAULT 1,

    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);


CREATE TABLE t_payment (

    pay_id BIGINT PRIMARY KEY,

    order_id BIGINT NOT NULL,

    pay_amount NUMERIC(12,2) NOT NULL,

    pay_status SMALLINT NOT NULL DEFAULT 0,

    pay_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);


CREATE TABLE t_order (

    order_id BIGINT PRIMARY KEY,

    user_id BIGINT NOT NULL,

    amount NUMERIC(12,2) NOT NULL DEFAULT 0,

    status SMALLINT NOT NULL DEFAULT 0,

    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);

```


## 6.3 插入测试数据


```sql

INSERT INTO t_user (user_id, username, mobile, status)

SELECT i, 'user_'||i, '138'||lpad(i::text,8,'0'), 1

FROM generate_series(1,50000) i;


INSERT INTO t_order (order_id, user_id, amount, status)

SELECT i, i%50000+1, ROUND((i*7.3)::numeric,2), i%4

FROM generate_series(1,200000) i;


INSERT INTO t_payment (pay_id, order_id, pay_amount, pay_status, pay_time)

SELECT i, i, ROUND((i*6.1)::numeric,2), i%3, CURRENT_TIMESTAMP

FROM generate_series(1,200000) i;


ANALYZE t_user;

ANALYZE t_order;

ANALYZE t_payment;

```


## 6.4 创建多表发布


```sql

CREATE PUBLICATION pub_business_module

FOR TABLE t_user, t_order, t_payment;

```


查看发布对象:


```sql

SELECT * FROM pg_publication_tables

WHERE pubname = 'pub_business_module';

```


## 6.5 在订阅端创建对应表


为保证结构一致,建议使用同一套 DDL 在订阅端执行。


```sql

\c fgedudb fgedu


CREATE TABLE t_user (

    user_id BIGINT PRIMARY KEY,

    username VARCHAR(128) NOT NULL,

    mobile VARCHAR(32),

    status SMALLINT NOT NULL DEFAULT 1,

    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);


CREATE TABLE t_payment (

    pay_id BIGINT PRIMARY KEY,

    order_id BIGINT NOT NULL,

    pay_amount NUMERIC(12,2) NOT NULL,

    pay_status SMALLINT NOT NULL DEFAULT 0,

    pay_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);


CREATE TABLE t_order (

    order_id BIGINT PRIMARY KEY,

    user_id BIGINT NOT NULL,

    amount NUMERIC(12,2) NOT NULL DEFAULT 0,

    status SMALLINT NOT NULL DEFAULT 0,

    create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

    update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP

);

```


## 6.6 创建多表订阅


```sql

CREATE SUBSCRIPTION sub_business_module

CONNECTION 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

PUBLICATION pub_business_module;

```


## 6.7 验证多表同步


发布端:


```sql

INSERT INTO t_user (user_id, username, mobile, status) VALUES (9999999, 'fengge_test', '13900000000', 1);

INSERT INTO t_order (order_id, user_id, amount, status) VALUES (9999999, 9999999, 888.00, 0);

INSERT INTO t_payment (pay_id, order_id, pay_amount, pay_status, pay_time) VALUES (9999999, 9999999, 888.00, 1, CURRENT_TIMESTAMP);

```


订阅端:


```sql

SELECT * FROM t_user WHERE user_id = 9999999;

SELECT * FROM t_order WHERE order_id = 9999999;

SELECT * FROM t_payment WHERE pay_id = 9999999;

```


## 6.8 多表复制中的依赖问题


如果表之间存在外键关系,订阅端应用变更时可能因为父表数据尚未到达而失败。因此,多表复制应尽量保证相关表同时纳入发布,并考虑在订阅端关闭外键约束检查,或通过应用层控制数据写入顺序。


---


# 7. 逻辑复制运维与监控


## 7.1 监控的重要性


逻辑复制创建完成并不代表项目结束。生产环境中必须持续监控复制延迟、复制槽状态、WAL 积压、订阅状态和错误日志。很多故障并不是复制链路突然中断,而是延迟缓慢增大,最终影响业务切换和数据一致性目标。


## 7.2 查看订阅状态


```sql

SELECT subname, subenabled, subconninfo, subpublications

FROM pg_subscription;

```


## 7.3 查看复制槽状态


```sql

SELECT slot_name, slot_type, active, restart_lsn, confirmed_flush_lsn

FROM pg_replication_slots;

```


## 7.4 计算复制延迟


发布端执行:


```sql

SELECT now() - pg_stat_replication.write_lag AS write_lag,

       now() - pg_stat_replication.replay_lag AS replay_lag

FROM pg_stat_replication;

```


订阅端执行:


```sql

SELECT subname, received_lsn, latest_end_lsn,

       latest_end_time - received_time AS apply_lag

FROM pg_stat_subscription;

```


## 7.5 监控 WAL 积压


```sql

SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retention_size

FROM pg_replication_slots;

```


如果 `retention_size` 持续增长,说明订阅端消费速度慢于发布端产生速度,需要排查订阅端负载、网络和磁盘 IO。


## 7.6 查看复制错误日志


```bash

tail -f /fgedudb/pg_log/postgresql-*.log

```


常见错误包括:


- 表结构不一致;

- 主键冲突;

- 外键约束失败;

- 复制槽缺失;

- 连接认证失败;

- 网络中断导致订阅失效。


## 7.7 暂停与恢复订阅


```sql

ALTER SUBSCRIPTION sub_business_module DISABLE;

ALTER SUBSCRIPTION sub_business_module ENABLE;

```


暂停订阅后,发布端仍会保留复制槽位置,因此必须关注 WAL 保留空间。


## 7.8 删除订阅与复制槽


如果项目下线,应先删除订阅,再根据业务需要决定是否删除复制槽。


```sql

DROP SUBSCRIPTION IF EXISTS sub_business_module;

```


发布端删除复制槽:


```sql

SELECT pg_drop_replication_slot('sub_business_module');

```


## 7.9 生产运维建议


生产环境建议建立以下巡检项:


1. 复制延迟是否超过业务容忍阈值;

2. 复制槽是否活跃;

3. WAL 保留空间是否异常增长;

4. 订阅端错误日志是否有冲突;

5. 发布端与订阅端表结构是否一致;

6. 网络带宽和磁盘 IO 是否成为瓶颈。


---


# 8. pglogical 扩展配置实战


## 8.1 pglogical 简介


pglogical 是 PostgreSQL 生态中常用的逻辑复制扩展,适用于复杂数据同步、数据迁移和多源汇聚场景。它提供了更灵活的节点管理、复制集、冲突处理和双向复制能力。


## 8.2 安装 pglogical


安装时需要注意 PostgreSQL 版本与扩展版本兼容。本套风哥教程按统一项目环境进行安装。


```bash

yum install -y pglogical

```


或从源码包安装:


```bash

tar -zxf pglogical.tar.gz

cd pglogical

make

make install

```


## 8.3 修改共享库配置


```ini

shared_preload_libraries = 'pglogical'

```


修改后重启数据库:


```bash

pg_ctl restart -D /fgedudb/pgdata

```


## 8.4 创建扩展


```sql

CREATE EXTENSION pglogical;

```


验证安装:


```sql

SELECT extname FROM pg_extension WHERE extname = 'pglogical';

```


## 8.5 在发布端创建节点


```sql

SELECT pglogical.create_node(

    node_name := 'provider_node',

    dsn := 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

);

```


## 8.6 在订阅端创建节点


```sql

SELECT pglogical.create_node(

    node_name := 'subscriber_node',

    dsn := 'host=fgedu-net-cn2 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

);

```


## 8.7 创建复制集


```sql

SELECT pglogical.create_replication_set(

    set_name := 'rs_business',

    replicate_insert := true,

    replicate_update := true,

    replicate_delete := true,

    replicate_truncate := false

);

```


## 8.8 将表加入复制集


```sql

SELECT pglogical.replication_set_add_table('rs_business', 't_order');

SELECT pglogical.replication_set_add_table('rs_business', 't_user');

SELECT pglogical.replication_set_add_table('rs_business', 't_payment');

```


## 8.9 在订阅端创建订阅


```sql

SELECT pglogical.create_subscription(

    subscription_name := 'sub_pglogical_business',

    provider_dsn := 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025',

    replication_sets := ARRAY['rs_business']

);

```


## 8.10 查看 pglogical 状态


```sql

SELECT * FROM pglogical.node;

SELECT * FROM pglogical.replication_set;

SELECT * FROM pglogical.subscription;

```


## 8.11 pglogical 常见问题


### 8.11.1 扩展未正确加载


如果创建扩展时报错,通常是 `shared_preload_libraries` 未配置或数据库未重启。


### 8.11.2 表结构不一致


pglogical 同样要求发布端和订阅端表结构对齐,否则会出现列类型不匹配错误。


### 8.11.3 冲突处理


在双向复制或多源汇聚中,冲突处理非常关键。可以通过冲突检测策略、时间戳策略或应用层去重机制降低冲突风险。


---


# 9. 综合实战一:同步模式、表结构与表数据


## 9.1 实战背景


很多项目不是简单创建一条复制链路,而是需要同时完成“结构同步规划、数据初始化和增量同步”三个环节。本套风哥教程本文通过一个完整项目案例,演示如何将业务模块从 `fgedu-net-cn1` 迁移到 `fgedu-net-cn2`。


## 9.2 项目实施步骤


### 步骤一:确认业务范围


需要同步的业务表如下:


- `t_user`

- `t_order`

- `t_payment`

- `t_product`

- `t_stock`


### 步骤二:确认复制模式


本项目采用“初始结构同步 + 全量数据导入 + 增量逻辑复制”的模式。


### 步骤三:导出发布端表结构


```bash

pg_dump -h fgedu-net-cn1 -U fgedu -d fgedudb \

  -s -t t_user -t t_order -t t_payment -t t_product -t t_stock \

  -f /fgedudb/backup/schema_business.sql

```


### 步骤四:导入订阅端表结构


```bash

psql -h fgedu-net-cn2 -U fgedu -d fgedudb \

  -f /fgedudb/backup/schema_business.sql

```


### 步骤五:导出发布端业务数据


```bash

pg_dump -h fgedu-net-cn1 -U fgedu -d fgedudb \

  -a -t t_user -t t_order -t t_payment -t t_product -t t_stock \

  -f /fgedudb/backup/data_business.sql

```


### 步骤六:导入订阅端业务数据


```bash

psql -h fgedu-net-cn2 -U fgedu -d fgedudb \

  -f /fgedudb/backup/data_business.sql

```


### 步骤七:在发布端创建发布


```sql

CREATE PUBLICATION pub_project_business

FOR TABLE t_user, t_order, t_payment, t_product, t_stock;

```


### 步骤八:在订阅端创建订阅


```sql

CREATE SUBSCRIPTION sub_project_business

CONNECTION 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

PUBLICATION pub_project_business

WITH (copy_data = false);

```


这里使用 `copy_data = false`,因为数据已经通过 `pg_dump` 初始化完成。


### 步骤九:验证数据一致性


```sql

SELECT count(*) FROM t_order;

SELECT count(*) FROM t_user;

SELECT count(*) FROM t_payment;

```


分别在发布端和订阅端执行,确认记录数一致。


### 步骤十:验证增量同步


发布端:


```sql

INSERT INTO t_product (product_id, product_name, price, status)

VALUES (99999, 'fengge_product', 999.00, 1);

```


订阅端:


```sql

SELECT * FROM t_product WHERE product_id = 99999;

```


## 9.3 项目切换建议


当复制延迟稳定且业务验证通过后,可以在低峰期完成应用切换。切换前应确认:


- 发布端与订阅端数据一致;

- 复制延迟在可接受范围内;

- 应用连接字符串已准备好;

- 回滚方案已明确;

- 业务方已确认切换窗口。


---


# 10. 综合实战二:多对一数据汇聚


## 10.1 实战背景


多对一汇聚是逻辑复制的典型高级场景。多个业务源将各自的业务数据同步到同一个目标库,用于报表分析、统一查询或数据中台建设。


## 10.2 环境规划


假设存在两个源端和一个目标端:


| 角色 | 主机名 | 说明 |

|---|---|---|

| 源端1 | `fgedu-net-cn1` | 业务库 A |

| 源端2 | `fgedu-net-cn2` | 业务库 B |

| 目标端 | 新增目标主机 | 汇聚库 |


为便于演示,本套风哥教程本文继续使用 `fgedu-net-cn1` 和 `fgedu-net-cn2` 作为两个源端,目标端逻辑库仍使用 `fgedudb`。


## 10.3 在源端1创建发布


```sql

CREATE PUBLICATION pub_source1 FOR TABLE t_order, t_user;

```


## 10.4 在源端2创建发布


```sql

CREATE PUBLICATION pub_source2 FOR TABLE t_order, t_user;

```


## 10.5 在目标端创建订阅1


```sql

CREATE SUBSCRIPTION sub_source1

CONNECTION 'host=fgedu-net-cn1 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

PUBLICATION pub_source1;

```


## 10.6 在目标端创建订阅2


```sql

CREATE SUBSCRIPTION sub_source2

CONNECTION 'host=fgedu-net-cn2 port=5432 dbname=fgedudb user=fgedu password=Fgedu@2025'

PUBLICATION pub_source2;

```


## 10.7 处理主键冲突


多对一汇聚最大的问题是主键冲突。如果两个源端使用相同主键范围,目标端无法判断哪条记录为准。


常用解决方案:


1. 在业务层设计全局唯一 ID;

2. 在表中增加来源标识列;

3. 使用复合主键;

4. 应用层预先做 ID 段隔离。


示例:


```sql

ALTER TABLE t_order ADD COLUMN source_id VARCHAR(32);

UPDATE t_order SET source_id = 'fgedu-net-cn1';

```


目标端将主键改为 `(order_id, source_id)`:


```sql

ALTER TABLE t_order DROP CONSTRAINT t_order_pkey;

ALTER TABLE t_order ADD PRIMARY KEY (order_id, source_id);

```


## 10.8 验证汇聚结果


```sql

SELECT source_id, count(*) FROM t_order GROUP BY source_id;

```


如果两个源端数据都已进入目标端,说明多对一汇聚链路已建立。


## 10.9 多对一项目注意事项


- 必须提前设计主键策略;

- 必须明确数据冲突规则;

- 必须监控每个源端的延迟;

- 必须避免业务直接写入目标端造成数据混乱;

- 必须保留来源标识,便于问题追溯。


---


# 11. 风哥针对本文总结


PostgreSQL 逻辑复制是一项理论性与实践性都很强的数据库技术。本套风哥教程从逻辑复制的基础概念、核心架构、发布订阅模型、复制槽机制开始,逐步进入项目环境准备、单表复制、多表复制、运维监控、pglogical 扩展以及综合项目实战。


风哥认为,真正掌握逻辑复制,不能只停留在创建发布和订阅的命令层面,而要理解 WAL 解析、复制槽位置、事务顺序、冲突处理、监控告警和切换流程。生产环境中的逻辑复制项目,通常不是“能不能复制”的问题,而是“能不能稳定复制、能不能处理冲突、能不能在业务窗口内完成切换”的问题。


从实战角度看,单表复制是基础,多表复制是常态,多对一汇聚是进阶。每一层都需要更强的结构治理、数据校验和运维能力。本套风哥教程中涉及的主机名、路径、数据库名、用户名和参数配置,都是为了帮助读者形成可复用的项目模板。只要理解这些模板背后的设计思想,后续就可以根据实际业务规模进行调整。


风哥针对本文总结如下:


第一,逻辑复制适合部分表同步、数据迁移、多源汇聚和跨版本数据同步,但不适合替代所有高可用场景。


第二,发布端、订阅端和复制槽是逻辑复制的铁三角。发布端负责产生变更,订阅端负责应用变更,复制槽负责记录消费位置。


第三,环境参数必须按服务器规格规划。本套风哥教程基于 64G 内存、8CPU 环境,对共享内存、工作内存、WAL 日志和复制进程数进行了合理配置。


第四,单表复制是项目起点,多表复制是项目主体。真实业务通常是多张表协同复制,必须关注外键依赖、数据到达顺序和结构一致性。


第五,监控是生产项目的生命线。复制延迟、复制槽状态、WAL 积压、错误日志和冲突处理,决定了逻辑复制链路能否长期稳定运行。


第六,pglogical 扩展提供更复杂的复制能力,但也引入更高的配置复杂度。是否使用 pglogical,应根据项目复杂度、冲突策略和团队维护能力决定。


第七,综合项目必须按步骤实施。结构导出、数据初始化、增量配置、数据校验、业务验证和切换计划,缺一不可。


第八,多对一汇聚的核心不是复制命令,而是主键策略和冲突规则。没有清晰的数据归属策略,多源汇聚一定会出现故障。


最后,风哥强调:数据库架构设计的目标不是追求复杂技术,而是用最合适的技术解决业务问题。逻辑复制的价值,在于它能把数据变化以更灵活的方式组织起来,为业务迁移、系统改造和数据平台建设提供支撑。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9664900/viewspace-3186257/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
风哥Oracle,MySQL,PostgreSQL,Linux,大数据,国产数据库,中间件等教程

注册时间:2010-10-26

  • 博文量
    78
  • 访问量
    406512