PostgreSQL13.1双Schema双向逻辑复制的无限循环问题咨询
会触发无限循环。
PostgreSQL逻辑复制的默认机制会将订阅端应用到表上的所有变更写入WAL日志,如果你同时在两侧配置了指向对方的发布-订阅关系,schema1.table1的本地变更同步到schema2.table2后,schema2侧的发布规则会把这条同步产生的变更再次发回schema1,循环往复,直到事务ID耗尽或磁盘空间占满,该问题和是否存在并发修改同一条数据的场景无关,只要双向发布订阅规则未做特殊限制就会触发。
以下方案按实施复杂度从低到高排序:
方案1:利用复制起源(Origin)跟踪机制原生规避
PostgreSQL逻辑复制默认会给所有通过订阅同步过来的变更打上origin标记,发布规则默认不会二次发布带有origin标记的WAL记录,你只需在创建两侧订阅时保持默认参数origin = true即可,无需修改表结构或业务逻辑。
如果你已经创建了订阅,可以执行以下命令确认配置:-- 查看所有订阅的origin配置 SELECT subname, suborigin FROM pg_subscription; -- 如果suborigin为none,修改为启用origin跟踪 ALTER SUBSCRIPTION 你的订阅名称 SET (origin = true);注意:同实例跨schema的逻辑复制场景也支持该机制,无需额外适配。
方案2:增加来源标记字段做行过滤
如果你需要更可控的同步规则,可以通过新增字段结合PostgreSQL 13支持的发布行过滤功能实现:- 给两侧表新增来源标记字段:
ALTER TABLE schema1.table1 ADD COLUMN data_origin TEXT NOT NULL DEFAULT 'schema1'; ALTER TABLE schema2.table2 ADD COLUMN data_origin TEXT NOT NULL DEFAULT 'schema2';- 调整两侧发布规则,仅发布本地产生的变更:
-- 修改schema1侧的发布,仅同步origin为schema1的行变更 ALTER PUBLICATION pub_schema1 SET TABLE schema1.table1 WHERE (data_origin = 'schema1'); -- 修改schema2侧的发布,仅同步origin为schema2的行变更 ALTER PUBLICATION pub_schema2 SET TABLE schema2.table2 WHERE (data_origin = 'schema2');该方案优势是规则透明,适合需要自定义同步逻辑的场景。
方案3:按操作用户过滤发布内容
如果你的业务只有Django应用用户会写入这两张表,可以直接在发布规则中过滤掉复制用户产生的变更:-- 替换为你的Django业务访问用户和发布名称 ALTER PUBLICATION pub_schema1 SET TABLE schema1.table1 WHERE (current_user = 'django_app_user'); ALTER PUBLICATION pub_schema2 SET TABLE schema2.table2 WHERE (current_user = 'django_app_user');该方案无需修改表结构,适合业务写入入口固定的场景。
注意:你当前确认无同一条数据的并发修改场景,无需额外配置冲突处理规则;如果后续业务调整存在冲突可能,可以将订阅的
conflict_resolution参数设置为'apply_remote'或自定义冲突触发器解决。
内容的提问来源于stack exchange,提问作者kallada

