PostgreSQL逻辑复制(Logical Replication)从10版本开始原生支持,与物理流复制不同,逻辑复制工作在表级别,以发布/订阅模型传输INSERT/UPDATE/DELETE操作的逻辑变更,不依赖Block级别的WAL一致性。这一特性使其在跨版本升级、异构数据库同步、读写分离等场景中具有独特优势。本文围绕逻辑复制Slot的配置、数据同步拓扑搭建和常见故障排查展开。
PostgreSQL逻辑复制与物理流复制架构对比
物理流复制传输WAL日志的原始Block变更,备库是主库的字节级副本,要求主备版本一致、架构一致。逻辑复制则通过逻辑解码插件(pgoutput)将WAL解析为行级别的变更事件,订阅端解析后执行SQL:
| 特性 | 物理流复制 | 逻辑复制 |
|---|---|---|
| 复制粒度 | 整个实例 | 选定的表 |
| 版本要求 | 必须同版本 | 支持跨版本 |
| 备库可写 | 不可写(只读) | 可写 |
| 复制内容 | 所有变更(含DDL) | 仅DML(INSERT/UPDATE/DELETE) |
| 跨平台 | 不支持 | 支持(不同OS/架构) |
| 网络中断恢复 | 自动追WAL | 依赖Replication Slot |
逻辑复制不支持DDL复制(建表、改表结构需手动同步),这是其主要限制。但在跨版本平滑升级(如PG13升PG16)、数据订阅消费(同步到ES/数仓)等场景中,逻辑复制是唯一原生方案。
逻辑复制发布端配置与WAL日志格式设置
逻辑复制要求主库将wal_level设置为logical,并调整WAL相关参数:
-- postgresql.conf (发布端)
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10
max_worker_processes = 20
-- 重载配置
SELECT pg_reload_conf();
-- 确认逻辑复制已启用
SHOW wal_level;
-- 应返回: logical
创建发布(Publication),指定要复制的表和操作类型:
-- 创建发布,包含所有表的所有DML操作
CREATE PUBLICATION pub_app_data FOR ALL TABLES;
-- 精确指定表和操作
CREATE PUBLICATION pub_orders FOR TABLE
orders, order_items, customers
WITH (publish = 'insert, update, delete');
-- 添加/移除表
ALTER PUBLICATION pub_orders ADD TABLE products;
ALTER PUBLICATION pub_orders DROP TABLE customers;
-- 查看发布状态
SELECT * FROM pg_publication;
SELECT * FROM pg_publication_tables;
被复制的表必须有REPLICA IDENTITY——用于标识UPDATE/DELETE操作对应行的主键或唯一索引。默认使用主键:
-- 无主键的表需设置replica identity
ALTER TABLE orders REPLICA IDENTITY USING INDEX idx_orders_no;
-- 查看所有表的replica identity设置
SELECT relname, relreplident
FROM pg_class
WHERE relkind = 'r' AND relname IN ('orders', 'order_items');
relreplident值含义:d=默认(主键)、f=full(整行)、i=索引、n=无。设置为full时逻辑复制会传输整行作为匹配条件,对宽表性能影响较大。
逻辑复制Slot创建与订阅端数据初始化
订阅端需要创建订阅(Subscription),指定连接信息和订阅的发布名称:
-- 订阅端 PostgreSQL 配置
-- postgresql.conf
max_replication_slots = 10
max_logical_replication_workers = 10
max_worker_processes = 20
-- 创建数据库和表结构(需手动同步结构)
CREATE DATABASE app_data;
-- 在订阅端创建与发布端一致的表结构
-- (DDL不会自动复制,必须手动执行)
-- 创建订阅
CREATE SUBSCRIPTION sub_app_data
CONNECTION 'host=10.0.1.10 port=5432 dbname=app_data user=replicator password=xxx'
PUBLICATION pub_app_data
WITH (
copy_data = true, -- 初始化时同步现有数据
create_slot = true, -- 自动创建replication slot
enabled = true,
slot_name = 'sub_app_data_slot',
synchronous_commit = on
);
-- 查看订阅状态
SELECT * FROM pg_subscription;
SELECT * FROM pg_stat_subscription;
copy_data = true会在创建订阅时执行一次COPY操作,将发布端现有数据同步到订阅端。对于大表,这个初始同步会占用较多IO和网络带宽,建议在低峰期执行。
跨版本数据同步与无缝迁移操作流程
逻辑复制最典型的应用场景是PostgreSQL跨大版本升级。以PG13迁移到PG16为例:
# 步骤1: 准备PG16新实例,导入表结构
pg_dump -h old-pg13-host -U postgres --schema-only --no-owner app_data | psql -h new-pg16-host -U postgres app_data
# 步骤2: 在PG13上创建发布
psql -h old-pg13-host -U postgres -d app_data -c "
CREATE PUBLICATION pub_migration FOR ALL TABLES;
"
# 步骤3: 在PG16上创建订阅,启用初始数据同步
psql -h new-pg16-host -U postgres -d app_data -c "
CREATE SUBSCRIPTION sub_migration
CONNECTION 'host=old-pg13-host port=5432 dbname=app_data user=replicator password=xxx'
PUBLICATION pub_migration
WITH (copy_data = true, create_slot = true);
"
# 步骤4: 监控同步进度
watch -n 5 "psql -h new-pg16-host -U postgres -d app_data -c "
SELECT subname, received_lsn, latest_end_lsn,
latest_end_time,
NOW() - latest_end_time AS replication_lag
FROM pg_stat_subscription;
""
# 步骤5: 同步追上后,切换应用连接到PG16,然后删除订阅
psql -h new-pg16-host -U postgres -d app_data -c "
ALTER SUBSCRIPTION sub_migration DISABLE;
DROP SUBSCRIPTION sub_migration;
"
逻辑复制Slot积压故障排查与WAL膨胀处理
逻辑复制最常见的故障是Replication Slot积压导致WAL文件无限增长。当订阅端长时间宕机,主库为保持WAL不被回收,pg_wal目录会持续膨胀直到磁盘满。
-- 检查所有slot的状态和WAL积压量
SELECT slot_name, plugin, slot_type, active,
restart_lsn,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag_pretty
FROM pg_replication_slots;
-- 输出示例:
-- slot_name | active | lag_bytes | lag_pretty
-- sub_app_data_slot | f | 8589934592 | 8192 MB
-- (active=f表示订阅端未连接,积压了8GB WAL)
处理方案:
-- 方案1: 临时推进slot的确认位置(跳过积压的WAL,数据会丢失)
SELECT pg_replication_slot_advance('sub_app_data_slot', pg_current_wal_lsn());
-- 方案2: 如果订阅端已永久不可用,删除slot
SELECT pg_drop_replication_slot('sub_app_data_slot');
-- 方案3: 设置最大WAL保留,防止磁盘满(PG13+)
-- postgresql.conf
-- max_slot_wal_keep_size = 10GB -- slot最多保留10GB WAL,超出后slot变为inactive
-- 监控告警:WAL积压超过1GB告警
SELECT slot_name,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots
WHERE pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) > 1073741824;
生产环境中建议为每个逻辑复制配置max_slot_wal_keep_size,避免单个故障订阅拖垮主库。配合监控告警(Prometheus + postgres_exporter的pg_replication_slots指标),在积包量超过阈值时及时介入处理。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/postgresql-luo-ji-fu-zhi-slot-guan-li-yu-kua-ban-ben-shu-ju/