在PostgreSQL的逻辑复制架构中,发布端负责将预写日志中记录的变更解码成逻辑消息,订阅端通过复制槽消费这些消息并应用到本地表。一个容易被忽略的能力是:发布并不一定要包含整张表的所有行。通过给发布中的表附加WHERE条件,可以在源头过滤掉不需要的数据,让逻辑复制流只携带符合业务规则的行。这种机制非常适合只同步热点数据、按租户拆分数据、或者迁移部分历史记录等场景。理解这一机制的关键在于发布端行过滤表达式与复制槽消费的异步模型。

一、行过滤策略在发布端的表达方式
发布可以理解为一个数据推送的集合。PostgreSQL支持三种粒度的发布对象:整库所有表、指定表、以及指定表加行过滤条件。最灵活的是第三种,它在创建发布时通过FOR TABLE后面的WHERE子句来声明过滤表达式。发布端只需要保证wal_level参数为logical,并且运行一个WAL发送进程,就能将符合条件的行变更封装成逻辑消息。
过滤表达式必须是一个稳定的布尔表达式,可以使用表里的列,也可以使用内置函数,但不能包含子查询、聚合函数、非确定性函数或自定义的volatile函数。原因是逻辑复制的行过滤需要在解码WAL时反复求值,PostgreSQL无法提前预计算,也不能依赖查询优化器。一个常见的例子是只同步高优先级且未归档的订单:
-- 创建只包含高优先级未完成订单的发布
CREATE PUBLICATION high_priority_pub FOR TABLE orders
WHERE (priority = 'high' AND status IN ('pending', 'processing'));
如果发布已经存在,也可以使用ALTER PUBLICATION给某张表追加过滤条件。需要注意的是,一张表在同一个发布中只能有一种过滤表达式,并且修改过滤条件后,只有新的变更会遵循新规则,已经发送过的历史行不会自动重新发送。
二、异步复制槽如何配合过滤策略工作
逻辑复制默认就是异步的。发布端把变更写入WAL并完成事务提交后,不会等待订阅端确认。解码进程从这个WAL中读取记录,对发布表执行行过滤判断,符合条件的变更被转换成逻辑消息,写入对应的复制槽。复制槽可以理解为一个读取游标,它保存了订阅端已经消费到的位置,防止WAL在订阅端确认之前被清理。
这意味着发布端和订阅端之间的延迟主要由订阅端应用速度决定。如果订阅端负载较高或者网络抖动,复制槽会积压,发布端的WAL保留策略需要配合调整。参数wal_keep_size或者复制槽本身会阻止WAL被过早回收。一个典型的订阅端配置如下:
-- 在订阅端创建订阅,并指定连接信息 CREATE SUBSCRIPTION high_priority_sub CONNECTION 'host=192.168.10.10 port=5432 dbname=source user=replicator password=secret' PUBLICATION high_priority_pub;
订阅端通过后台的receiver进程连接到发布端,持续拉取复制槽中的消息,再交给apply进程在本地执行。由于是异步,发布端事务提交速度不会受到订阅端影响,但如果出现主备切换或发布端崩溃,未消费的WAL记录可能丢失,这是异步复制的固有代价。
另外,订阅端表结构需要与发布端兼容。过滤后的行到达订阅端后,如果表存在主键或唯一索引,默认按主键匹配更新和删除;如果没有主键,更新和删除操作需要额外的复制标识设置,否则会导致错误。
三、过滤策略的性能开销与常见问题
行过滤表达式在WAL解码阶段被执行,每一条涉及发布表的变更都要经过表达式计算。表达式本身无法使用索引,因为WAL记录中只有变更行的具体列值,PostgreSQL必须逐行判断。如果过滤条件复杂,例如包含多个正则表达式、类型转换或调用昂贵函数,解码速度会明显下降,甚至拖慢整个复制槽的消费进度。因此应该优先使用简单等值判断、范围判断,避免使用LIKE或者复杂逻辑。
还需要注意,行过滤只在逻辑复制流中生效,发布端WAL中依然会记录全表的所有变更。比如orders表每天插入一百万行,其中只有一万行满足priority = 'high',发布端仍会为一百万行生成WAL记录,解码时也会扫描这些记录来决定是否发送。行过滤节省的是网络传输和订阅端磁盘与CPU,而不是发布端WAL体积。如果发布端本身负载不高,这个开销通常可以接受;但如果发布端CPU紧张,建议将过滤逻辑下推到应用侧,或者使用分区表把热数据与冷数据分开,减少无效解码。
监控逻辑复制的常用方法是查询pg_stat_replication视图和pg_replication_slots视图。前者能看到WAL发送进程的同步状态、延迟字节数,后者能看到每个复制槽的restart_lsn、confirmed_flush_lsn以及是否积压。结合订阅端日志,可以判断过滤条件是否生效,是否存在长事务导致的复制槽膨胀。
还需要留意过滤条件调整后的数据一致性。比如原来没有过滤条件,后来加上priority = 'high',订阅端已经存在的低优先级行不会被自动删除,仍然保留在订阅端表中。如果需要彻底对齐,可以考虑在业务低峰期清空订阅端表并重新同步,或者使用逻辑复制之外的DML操作进行清理。
PostgreSQL逻辑复制行过滤异步复制修改时间:2026-09-18 15:34:04