PostgreSQL 逻辑复制(Logical Replication)是数据库层最实用的实时数据同步方案之一。与物理流复制整库拷贝不同,逻辑复制以”发布(Publication)”和”订阅(Subscription)”为单位,能够把单张表、甚至满足条件的部分行,近乎实时地推送到下游——无论是数据仓库、缓存层还是另一个微服务库。本文从核心概念、搭建步骤到生产避坑,带你完整跑通一条逻辑复制链路,并可与 PostgreSQL 慢查询优化 配合,先保证发布端不被慢 SQL 拖垮。
一、逻辑复制与流复制:到底差在哪
很多团队一上来就分不清”流复制”和”逻辑复制”。简单说,流复制(Streaming Replication)是物理级、整实例的 WAL 字节流同步,主备大版本必须一致,适合做高可用;逻辑复制是逻辑级、按表按行的变更同步,适合做数据分发。下面这张表把关键差异一次说清。
| 维度 | 物理流复制(Streaming) | 逻辑复制(Logical) |
|---|---|---|
| 复制粒度 | 整个实例 / 库 | 表级、行级、列级 |
| 跨大版本 | 不支持(主备需同版本) | 支持(12→15 可跨小版本同步) |
| DDL 是否同步 | 同步 | 不同步(需手动两端执行) |
| 典型用途 | 高可用、故障切换 | 数据分发、异构同步 |
逻辑复制的底层依赖”逻辑解码(Logical Decoding)”:PostgreSQL 把 WAL 中已提交的事务,通过解码插件(内置 pgoutput,或社区 wal2json / test_decoding)翻译成可读的 INSERT / UPDATE / DELETE 行事件。发布端把这些事件按发布定义过滤后发给订阅端,订阅端再回放。理解这一点就能明白:为什么 DDL 无法复制——DDL 本身不写在表行里,自然不会出现在逻辑变更流中。关于 PostgreSQL 与 SQL Server 在复制与架构上的差异,可参考 PostgreSQL vs SQL Server 迁移避坑。
二、核心对象:发布与订阅
逻辑复制只有两个核心角色:发布端(Publisher)把 WAL 中已提交的事务按表解码成逻辑变更,订阅端(Subscriber)消费这些变更并回放。一个发布可以挂载多张表,一个订阅对应一个发布连接。一个订阅在发布端会占用一个”复制槽(Replication Slot)”,它负责记住已经发送到哪了,避免 WAL 被提前回收——这也是后面”槽不清理会撑爆磁盘”的伏笔。
2.1 发布端配置
先在 postgresql.conf 打开逻辑解码,并在 pg_hba.conf 放行复制账号。复制通道强烈建议走加密,证书方案可看 Let’s Encrypt 免费 SSL 证书自动续期。注意 wal_level=logical 需要重启实例才能生效。
-- postgresql.conf
wal_level = logical
max_wal_senders = 10
max_replication_slots = 10
-- pg_hba.conf 追加(复用 ssl 加密通道)
host replication repl_user 10.0.0.0/8 scram-sha-256
2.2 创建发布与订阅
在发布库建发布,指定要同步的表;在订阅库用 CONNECTION 指向发布端。CREATE SUBSCRIPTION 默认 copy_data=true,会先做一次全量初始拷贝,再追增量。
-- 发布端:同步 orders 表
CREATE PUBLICATION orders_pub FOR TABLE orders;
-- 订阅端:指向发布端连接串(默认先全量拷贝再追增量)
CREATE SUBSCRIPTION orders_sub
CONNECTION 'host=10.0.0.5 port=5432 dbname=app user=repl_user password=**** sslmode=require'
PUBLICATION orders_pub;
三、行级过滤与列筛选
PostgreSQL 15 起支持在发布上做行级过滤与列筛选,让”只同步 VIP 用户订单”这类需求无需在应用层处理。订阅端表可以比发布端多出额外列(额外列不会被填充),这让分发改表结构更平滑。但行级过滤要求表具备 REPLICA IDENTITY:DEFAULT 用主键定位旧行,INDEX 用唯一索引,FULL 用整行旧值——无主键表只能选 FULL。
-- 仅同步金额大于 1000 的订单行
CREATE PUBLICATION vip_orders_pub
FOR TABLE orders WHERE (amount > 1000);
-- 仅发布部分列,降低下游存储压力
CREATE PUBLICATION cols_pub
FOR TABLE users (id, name, email);
四、生产避坑:三道必考题
4.1 没有主键的表会”慢”且”危险”
逻辑复制的 UPDATE / DELETE 靠主键定位目标行。无主键表需 ALTER TABLE t REPLICA IDENTITY FULL;,但这意味着把整行旧值写进 WAL,复制流量与冲突风险都会上升。为已有表补主键是最优解;若实在无法加主键,至少评估 FULL 带来的 WAL 膨胀与更新冲突,并在订阅端准备好冲突解决策略。
4.2 DDL 不会自动同步
在发布端 ALTER TABLE 加字段,订阅端不会自动跟随。务必把 DDL 纳入变更流程:先计算发布端执行、再在订阅端执行、最后用 pg_stat_subscription 验证 last_error_message 为空。推荐顺序反过来更稳妥——先在订阅端加列,再在发布端加列,避免订阅端因列缺失而中断。
4.3 复制槽泄漏导致 WAL 膨胀
删除订阅必须走 DROP SUBSCRIPTION,它会自动清理发布端的复制槽。若只删库、断网或误删槽关联,发布端会留下一个 active=false 却仍”保留 WAL”的孤儿槽,导致磁盘被 WAL 持续占满。定期巡检 pg_replication_slots 的 active 与 retained_wal,发现孤儿槽及时清理。
五、典型落地场景
逻辑复制在以下场景收益最高,且与分区、缓存体系天然互补:
| 场景 | 逻辑复制的价值 | 关联实践 |
|---|---|---|
| 数据仓库入湖 | 增量同步,免去全量抽取 | 配合 PostgreSQL 分区表实战 提升大表性能 |
| 缓存一致性 | 变更即失效,降低脏读 | 思路可借鉴 binlog 订阅式缓存一致性 |
| 微服务解耦 | 只读副本按需订阅 | 跨库数据按需分发,避免接口拉取 |
六、常见故障排查表
实战中最常遇到以下四类问题,按表对照处理即可:
| 现象 | 根因 | 处理 |
|---|---|---|
| 订阅中断,last_error_message 报列不匹配 | DDL 不同步 | 在订阅端补相同 DDL 后重试 |
| lag 持续增长 | 订阅端写入慢或网络抖动 | 提升 applier worker 数、扩容订阅端 |
| 发布端磁盘暴涨 | 孤儿复制槽未清理 | 清理 inactive 槽释放 WAL |
| 初始拷贝极慢 | 大表 copy_data 全量 | 分批或关闭 copy_data 手动导入 |
七、监控与运维
复制是否正常,靠两张系统视图说话。下面查询能直接看到槽位积压与订阅端回放延迟——注意 lag 短暂大于 0 是正常的,持续增大才需介入:
-- 发布端:确认槽位在消费、无积压
SELECT slot_name, active,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag
FROM pg_replication_slots;
-- 订阅端:查看最近错误与回放延迟
SELECT subname, received_lsn, latest_end_lsn, last_error_message
FROM pg_stat_subscription;
若 lag 持续增大,通常是订阅端写入慢或网络抖动;last_error_message 非空则多半是 DDL 不同步导致的列不匹配。把这两个查询接入监控(如 Prometheus),就能在复制中断前收到告警。
八、用 SSL 加密复制通道
复制账号凭证与数据明文传输风险极高。生产环境务必在连接串加 sslmode=require,并用受信任证书为数据库节点加密,避免自签证书被中间人劫持。证书托管可参考 Let’s Encrypt 证书自动续期方案,把续期做成无人值守的定时任务,复制链路就不会因证书过期而半夜中断。
九、小结
PostgreSQL 逻辑复制用”发布—订阅”模型把数据同步的粒度精细到表与行,是构建实时数据管道、缓存失效、数据仓库入湖的基石能力。落地时牢记三条铁律:发布端开 wal_level=logical、给表补主键、把 DDL 纳入变更流程;再补上一条运维底线——定期清理孤儿复制槽。配合慢查询优化、分区表与加密通道,你的复制链路就能稳稳跑在生产上。




