1. 逻辑复制全景概览

PostgreSQL 的复制能力分为两条主线:物理复制与逻辑复制。物理复制以 WAL(Write-Ahead Log)为搬运对象,把主库磁盘页的字节级变化原样送到备库,因此备库是主库的“块级镜像”,只能整实例复制,无法只挑一张表。逻辑复制则不同,它把 WAL 解码成一行行的逻辑变更(INSERT/UPDATE/DELETE/TRUNCATE),再以表为单位投递到订阅端,由订阅端重新执行这些变更。理解这一层“字节流 vs 行变更”的差异,是掌握逻辑复制全部特性的起点。

逻辑复制的定义与本质

逻辑复制是 PostgreSQL 10 引入的原生特性,它基于发布/订阅模型:发布端定义“发布”(Publication),声明哪些表的哪些操作需要被复制;订阅端定义“订阅”(Subscription),声明从哪个发布端、哪个发布拉取数据。发布端把变更写入复制槽,订阅端通过逻辑复制协议拉取并应用。整个过程对表结构有要求——被复制的表必须有主键或唯一约束(用于定位 UPDATE/DELETE 的目标行),否则更新和删除无法正确路由。

与物理复制的本质区别可以归纳为四点:复制粒度上,物理复制是实例级,逻辑复制是表级;复制内容上,物理复制传 WAL 块,逻辑复制传解码后的行变更;版本兼容上,物理复制要求主备大版本一致,逻辑复制允许跨大版本(如 PG 14 发布到 PG 16 订阅);拓扑能力上,物理复制是单向流复制,逻辑复制支持多主汇聚、双向同步、部分表复制等更灵活的拓扑。

核心组件与数据流

逻辑复制链路上有五个关键角色。发布端侧:Publication 是变更的“筛选器”,决定哪些表、哪些操作(insert/update/delete/truncate)进入复制流;Replication Slot(逻辑复制槽)是变更的“缓冲区”,它记录订阅端已消费到的 LSN 位置,防止发布端过早回收 WAL;walsender 是发布端的发送进程,负责从复制槽读取变更并通过逻辑复制协议发给订阅端。订阅端侧:Subscription 是拉取配置,记录连接串、发布名、复制槽名;apply worker 是订阅端的应用进程,接收变更并在本地表上执行,同时把进度写回订阅端的复制源状态。

数据流的方向是:业务写入发布端表 → WAL 落盘 → 逻辑解码插件(默认 pgoutput)把 WAL 解码为行变更 → walsender 从复制槽读取并发送 → 订阅端 apply worker 接收并应用 → 订阅端表数据更新。这条链路中,复制槽是“背压”的关键:如果订阅端长时间不消费,复制槽会持续保留 WAL,导致发布端磁盘增长,这是运维中最需要监控的点。

逻辑复制数据流:发布端 → 复制槽 → walsender → apply worker → 订阅端 发布端 Publisher 业务表 orders / customers INSERT / UPDATE / DELETE / TRUNCATE WAL 日志 记录所有数据变更的物理日志 逻辑解码 pgoutput WAL 字节 → 行级逻辑变更 Publication 表与操作筛选 Replication Slot 保留 WAL 与 LSN walsender 发送进程 逻辑复制协议 读取变更 订阅端 Subscriber Subscription 连接串 / 发布名 / 槽名 apply worker 接收并应用行变更 订阅端表 orders / customers 数据最终一致 复制源状态 记录已应用 LSN 进度 变更流 复制槽是背压关键:订阅端不消费 → 发布端 WAL 持续保留 → 磁盘增长,需监控 pg_replication_slots 的 restart_lsn 与 pg_wal_lsn_diff

典型应用场景

逻辑复制最直接的价值是跨版本升级:把 PG 12 主库的数据通过逻辑复制同步到 PG 16 新库,验证业务后再切换,停机窗口可以压缩到分钟级。其次是部分表复制:只把订单、用户等核心表同步到报表库或数据仓库,避免整实例复制带来的冗余。第三是多主汇聚:多个区域库各自写入本地表,通过逻辑复制汇聚到一个中心库做全局分析。第四是双向同步:两个库互为发布者和订阅者,实现双活写入,但需要处理冲突(如主键冲突、更新冲突),通常配合冲突解决策略或业务层规避。第五是异构同步:通过逻辑解码插件把变更投递到 Kafka、消息队列或其他数据库,构建 CDC 管道。

与物理复制的选型对照

维度物理复制逻辑复制
复制粒度实例级(全部数据)表级(可挑选)
复制内容WAL 块(字节级)行变更(逻辑级)
版本要求主备大版本一致可跨大版本
拓扑能力单向流复制多主、双向、汇聚
DDL 同步自动同步不自动,需手动执行
序列同步自动同步不自动,需单独处理
典型延迟毫秒级毫秒到秒级
选型建议:如果目标是高可用容灾、要求备库与主库完全一致,优先物理复制;如果目标是跨版本迁移、部分表同步、多源汇聚或 CDC,逻辑复制更合适。两者可以并存,例如主库同时挂一个物理备库做容灾,再挂一个逻辑订阅做报表同步。

关键参数与观察点

发布端需要设置 wal_level = logical,这是逻辑复制的前提,修改后需重启实例。复制槽的 restart_lsn 表示订阅端尚未确认消费的最早 WAL 位置,pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) 可以估算积压的 WAL 字节数。订阅端可以通过 pg_stat_subscription 查看 apply worker 的状态、最后接收 LSN 和最后应用 LSN,两者的差值反映应用延迟。如果 last_msg_receipt_time 长时间不更新,说明订阅端没有收到新变更,需要检查网络或发布端 walsender 状态。

-- 发布端:查看复制槽积压
SELECT slot_name, plugin, slot_type, active,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots
WHERE slot_type = 'logical';

-- 订阅端:查看应用进度
SELECT subname, pid, relid, received_lsn, latest_end_lsn,
       now() - last_msg_receipt_time AS since_last_msg
FROM pg_stat_subscription;