PostgreSQL 逻辑复制是什么
PostgreSQL 的变更捕获基于 WAL 逻辑解码:把 wal_level 设为 logical 后,可以用发布/订阅(CREATE PUBLICATION / CREATE SUBSCRIPTION)在库之间同步表级变更,也可以用逻辑解码插件(内置 pgoutput、第三方 wal2json)把变更流交给外部工具。复制槽记录消费进度,这是它最需要留意的特性:槽建了没人消费,WAL 会一直不回收,直到把磁盘写满。
基本信息
| 前提配置 | wal_level=logical,修改后需要重启实例 |
|---|---|
| 发布订阅 | CREATE PUBLICATION 声明表,CREATE SUBSCRIPTION 订阅并复制 |
| 初始数据 | 订阅默认会复制初始表数据,目标表已有数据时应设为不复制初始数据 |
| 复制槽 | 记录消费位点;槽未推进则 WAL 不回收 |
| 行标识 | REPLICA IDENTITY 决定 UPDATE/DELETE 事件能带出多少旧值 |
| DDL 不同步 | 逻辑复制不传播表结构变更,DDL 需在两侧手工维护 |
典型使用场景
- 库到库同步:把业务库指定表实时同步到分析库或另一个实例
- CDC 接入:用解码插件把变更流交给外部采集工具,替代触发器方案
- 大版本升级过渡:用逻辑复制做跨版本迁移并缩短停机窗口
- 按表选择性同步:只复制关心的表,不复制全实例
- 多目标分发:同一份发布可被多个订阅端使用
发布订阅与复制槽
发布订阅与复制槽
-- 源端:必须为 logical 级别,修改参数后重启
SHOW wal_level;
-- 只发布需要同步的表
CREATE PUBLICATION pub_orders FOR TABLE app.orders;
-- 目标端:订阅发布(目标表已有数据时关闭初始复制)
CREATE SUBSCRIPTION sub_orders
CONNECTION 'host=10.0.0.5 dbname=app user=repl password=***'
PUBLICATION pub_orders
WITH (copy_data = false);
-- 源端查看槽状态与保留位点:active 为 false 时要警惕
SELECT slot_name, active, restart_lsn, confirmed_flush_lsn
FROM pg_replication_slots;旧值与槽清理
旧值与槽清理
-- UPDATE/DELETE 需要完整旧值时,把行标识设为 FULL
ALTER TABLE app.orders REPLICA IDENTITY FULL;
-- 删除不再使用的槽(会立即释放被该槽占住的 WAL)
SELECT pg_drop_replication_slot('sub_orders');
-- 观察 WAL 堆积与槽数量上限
SELECT COUNT(*) AS slot_cnt FROM pg_replication_slots;
SHOW max_replication_slots;关键配置与影响
| 配置 | 作用 | 影响 |
|---|---|---|
| wal_level | 决定能否逻辑解码 | 非 logical 时无法建发布与槽 |
| max_replication_slots | 实例允许的槽数量上限 | 数量不足时新槽创建失败 |
| REPLICA IDENTITY | 控制 UPDATE/DELETE 能带出的旧值 | 默认设置下旧值缺失,下游无法比对 |
| 复制槽活跃状态 | 指示是否有人在消费 | inactive 时 WAL 持续堆积 |
建槽后没人消费等于 WAL 一直不回收,直到写满磁盘导致实例拒绝写入:删除槽前先确认没有订阅仍在使用,删除操作不可回滚。
逻辑复制不传播 DDL:源端加列、改类型后必须在订阅端同步执行,否则复制会中断报错。把 DDL 变更纳入两侧统一的发布流程是关键。