PostgreSQL的逻辑复制基于发布与订阅模型,允许将特定表的数据变更以逻辑日志形式发送给订阅端。在离线场景下,例如订阅节点网络中断数小时甚至数天,主库依然在写入数据,复制槽会保留对应的WAL记录。如果不加任何过滤策略,订阅端恢复连接后会接收并应用这段时间内所有的INSERT、UPDATE、DELETE操作,这不仅占用带宽,还可能将无效或敏感数据同步到远端。因此,在离线环境中设计合理的过滤策略,是控制同步数据范围、降低资源消耗的关键。

发布端行过滤与列过滤的基本机制
从PostgreSQL 15开始,CREATE PUBLICATION支持使用WHERE子句对行进行过滤,也支持指定列列表来实现列过滤。行过滤在发布端完成,意味着不符合条件的元组根本不会进入逻辑解码输出,订阅端也就不会收到相关变更。对于离线场景,这能显著减少断连期间堆积在复制槽中的有效WAL量,因为被过滤掉的数据修改不会产生逻辑复制消息。
列过滤则通过只发布某些字段,降低每条变更记录的体量。例如一张用户表包含姓名、手机号、密码哈希等列,订阅端若仅需统计姓名分布,就可以只发布姓名列。需要注意的是,若表有UPDATE或DELETE操作,被过滤掉的列不能作为复制标识,否则订阅端无法定位行。此时必须配置REPLICA IDENTITY为FULL或使用唯一索引覆盖所需旧值。
以下示例创建一个仅发布活跃用户且只含id与name列的发布:
CREATE PUBLICATION user_filter_pub FOR TABLE users ( id, name ) WHERE (status = 'active');
在离线前若已建立该发布,那么断连期间非active用户的写入不会进入该发布对应的逻辑流,订阅端恢复后也无需处理这部分数据,过滤在源头生效。
离线期间复制槽与WAL保留策略的影响
逻辑复制依赖复制槽来记录订阅端的消费位置。当订阅离线,主库无法删除已被该槽消费的WAL,因而pg_wal目录会持续增长。若设置了max_slot_wal_keep_size,超过阈值后槽可能被标记为无效,导致离线恢复时订阅端报“slot not found”或数据丢失。因此,离线过滤策略必须配合容量规划:通过行过滤缩减变更量,可延缓WAL堆积速度。
另外,REPLICA IDENTITY决定了UPDATE和DELETE旧值如何记录。在离线恢复重放时,订阅端需要旧值匹配本地行。如果发布端使用默认REPLICA IDENTITY,仅主键旧值会被编码;当订阅端表结构因列过滤而不含主键时,必须改为FULL,否则重放报错。下面的代码展示如何修改表的复制标识:
ALTER TABLE users REPLICA IDENTITY FULL;
对于长时间离线,还可以定期在订阅端使用pg_logical_slot_get_changes观察槽积压,或在主库监控pg_replication_slots的wal_status字段,做到提前告警而非故障后处理。
订阅端冲突处理与离线过滤的综合实践
即便发布端做了过滤,订阅端在离线恢复后仍可能遇到冲突,比如本地已被其他系统删除了某行,而逻辑流带来UPDATE。此时可借助disable_on_error或自定义冲突处理函数。在过滤策略下,因为同步数据变少,冲突概率通常下降,但列过滤引发的行不匹配仍需重视。
实践中,我们常将行过滤与分区表结合。例如按时间分区的订单表,发布端只发布最近两个分区,离线系统仅同步热数据,历史分区由批量工具补齐。这样既保证逻辑复制链路轻量,又满足离线节点最终一致。代码层面,可在订阅端用触发器屏蔽特定错误:
CREATE OR REPLACE FUNCTION skip_conflict() RETURNS trigger AS $$ BEGIN RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER tr_skip AFTER INSERT ON users FOR EACH ROW EXECUTE FUNCTION skip_conflict();
总体而言,PostgreSQL逻辑复制在离线场景的过滤策略核心在于:发布端用WHERE与列定义裁剪数据,配合REPLICA IDENTITY保障重放正确,再辅以复制槽容量监控与订阅端冲突兜底。只有这样,才能在弱网、灾备或批量维护期间,既不让数据同步失控,也不因离线积压压垮主库。
PostgreSQL逻辑复制复制过滤修改时间:2026-08-18 02:04:26