PostgreSQL逻辑复制自10版本引入以来,成为构建数据分发、异构同步与微服务数据隔离的重要能力。很多团队在搭建订阅链路时,并不希望把整个数据库的所有表都推送到下游,而是只想同步其中几张核心表,甚至只同步表中满足某些业务条件的行。这就引出了逻辑复制里的过滤策略:表级过滤与行级过滤。理解这两种过滤的实现机制,能够帮助我们以更低的资源开销维持多条复制链路。

表级过滤:基于发布对象的白名单控制
表级过滤是PostgreSQL逻辑复制中最基础也最常用的过滤方式。它的核心思想非常简单:发布(publication)本身就是一个表对象的集合,只有被加入到发布中的表,其变更才会通过WAL日志被解码并发送给订阅端。我们在创建发布时使用FOR TABLE语法显式列出目标表,就能天然实现表级白名单。
例如,一个业务库里有订单表、用户表、日志表,但我们只想把订单和用户同步到报表库。此时可以单独建立一个发布,仅包含这两张表。底层实现上,逻辑解码插件(如pgoutput)在读取WAL记录时,会检查发生变更的表是否属于某个活跃发布,不属于则直接跳过。这种方式对性能影响极小,因为过滤动作发生在WAL解码阶段,不需要在事务执行时做额外判断。
需要注意,被加入发布的表必须具备合法的复制标识(REPLICA IDENTITY)。默认情况下,表以主键作为标识,若表无主键又需支持更新和删除的复制,则要设置为FULL或使用唯一索引。以下示例展示如何创建仅包含两张表的发布:
-- 创建仅包含核心表的发布 CREATE PUBLICATION report_pub FOR TABLE orders, users; -- 查看发布包含的表 SELECT pubname, schemaname, tablename FROM pg_publication_tables WHERE pubname = 'report_pub';
如果后期需要动态调整同步范围,可以使用ALTER PUBLICATION语句增删表。不过要记住,新增的表不会自动在订阅端创建,需要手动在订阅库建表并调用ALTER SUBSCRIPTION ... REFRESH PUBLICATION。表级过滤虽然简单,但无法处理同一张表内只同步部分行的需求,这就需要行级过滤来补充。
行级过滤:使用WHERE条件限定同步数据
PostgreSQL在15版本开始正式支持在发布中定义行过滤器(row filter),也就是在FOR TABLE后面附加WHERE子句,只有满足该条件的行变更才会进入逻辑复制流。这对于多租户系统尤其有用:我们可以让不同订阅端只接收属于自己租户的数据,而不必为每个租户建立独立数据库。
行过滤器的语法直接写在发布定义里,例如只同步状态为已支付的订单。解码插件在拼装变更事件时,会附加原始行的列值进行表达式求值,若不满足则丢弃。这里有一个关键限制:WHERE条件中只能引用表的列,不能使用子查询、易失函数或当前时间函数,否则发布创建会报错。同时,行过滤和复制标识紧密关联,如果某行因更新前的旧值不满足过滤条件但新值满足,是否能同步取决于旧行是否被识别。
下面示例创建一个只同步金额大于一百且未退款的订单的发布:
-- 行级过滤发布:仅同步有效大额订单 CREATE PUBLICATION paid_order_pub FOR TABLE orders WHERE (amount > 100 AND status <> 'refunded'); -- 订阅端建立对应表后创建订阅 CREATE SUBSCRIPTION sub_paid CONNECTION 'host=192.168.0.1 port=5432 dbname=src user=repl' PUBLICATION paid_order_pub;
在运维实践中,行级过滤会略微增加解码开销,因为每条变更都要做表达式计算。如果表写入量极大,建议配合表级过滤先缩小范围,再施加行过滤。另外,当使用REPLICA IDENTITY FULL时,更新操作的老值全部以JSON形式传出,行过滤器可基于老值或新值判断,但网络量会明显上升,需要权衡存储与准确性。
过滤策略的组合与常见陷阱
将表级与行级过滤组合,可以搭建非常灵活的数据分发网络。比如核心交易库定义多个发布:一个全表发布给灾备库,一个带行过滤的发布给风控库,一个仅含维度表的发布给BI库。订阅端通过不同的订阅名挂载不同发布,实现一对多且内容差异化的同步。这种架构比应用层双写更可靠,因为事务提交顺序由WAL保证,不会出现部分成功。
然而,组合过滤时容易踩到几个坑。首先,一个表可以同时属于多个发布,若某行满足A发布的过滤但不满足B发布,它只会出现在A的流里,这很合理,但如果你在订阅端用同一个表接收两个发布,需要确认不会有主键冲突或重复插入。其次,表结构变更(DDL)默认不会复制,当源端给表加列而发布WHERE条件引用了该列,必须重新刷新发布定义,否则解码会报列不存在。
另一个常见误区是认为过滤能替代权限控制。逻辑复制的发布和订阅依托于数据库角色,若复制用户有权读取全表,只是发布未包含某表,拥有超级权限的用户仍可直接查数据。因此过滤解决的是同步范围问题,不是安全隔离问题。最后建议对长事务频繁更新的表谨慎使用行过滤,因为未提交事务的变更会暂存在WAL里,解码延迟可能随着过滤计算堆积。通过监控pg_stat_replication与pg_logical_slot_get_changes可以及时发现瓶颈。
-- 查看订阅端复制状态与延迟 SELECT subname, received_lsn, latest_end_lsn FROM pg_stat_subscription; -- 在源端查看槽位积压 SELECT slot_name, pg_current_wal_lsn() - confirmed_flush_lsn AS lag FROM pg_replication_slots WHERE slot_type = 'logical';
综合来看,PostgreSQL逻辑复制的过滤策略以发布定义为支点,表级过滤负责宏观选型,行级过滤负责微观裁剪。规划时先梳理业务订阅关系,再用白名单缩小表集,最后用稳定的列条件过滤行。配合监控与合理的复制标识设置,即可在低带宽消耗下维持稳定的数据同步体系。
PostgreSQL逻辑复制复制过滤修改时间:2026-08-18 15:20:39