背景分析
在逻辑复制支持DDL的需求分析中,需要考虑如下场景:
逻辑复制支持同步指定列,当配置了同步指定列,又开启了ddl=table的时候,其行为预期可能有如下情况:
1.订阅端的ddl是同步完整的表结构,还是只同步指定的列;
2.订阅端的ddl如果同步完整的表结构,那么数据保持只同步指定列的数据,还是同步所有的列的数据;
参考mysql的实现,发布和订阅的表结构不用完全一致。当前的pg的逻辑复制也不要求表结构完全一致。
下面针对以上场景进行详细分析。
逻辑复制支持指定列同步
当前的逻辑复制并不要求发布端和订阅端的表结构完全相同,而且对于一张表的逻辑同步,可以只指定某个列或者某几个列的同步,即支持列级同步。其语法如下所示:
CREATE PUBLICATION pub FOR TABLE my_table (col1, col2);
- 只有
col1和col2的变更会被发送 - update 只会发送涉及的列
- INSERT 只发送指定列的值
列级复制有如下优势:
- 发布端表可以有额外列,订阅端未同步
- 只要订阅端有发布端发送的列,逻辑复制就能应用变更
- 使用列列表(
FOR TABLE (col1, col2))时:- 发布端 WAL 只发送这些列
- 订阅端表无需额外列
逻辑复制支持DDL的实现
逻辑复制表级同步支持自动DDL
在逻辑复制支持DDL的需求实现中,指定的语法格式如下:
CREATE PUBLICATION pub FOR ALL TABLES WITH (ddl='table,others');
或者对于单表:
CREATE PUBLICATION pub FOR TABLE my_table WITH (ddl='table,others');
通过ddl参数来指定是否开启ddl自动同步。
表级同步支持自动DDL分为两种情况:
- 订阅端表已经存在;
- 订阅端表不存在;
表级同步订阅端表已经存在
若发布端的表是订阅端的子集,且列名匹配,则可以正常复制。
比如发布端有a,b,c列,订阅端有a,b,c,d列(d列可以为空或者有默认值)。
且对于后续的列变更均可以自动同步,比如:
ALTER TABLE t ADD COLUMN c int;
ALTER TABLE t DROP COLUMN c;
若订阅端列少或者列名不匹配,则无法正常复制。
表级同步订阅端表不存在
通过重新执行捕获的客户端sql语句,在订阅端创建表,两端的表结构最终一致。
逻辑复制列级同步支持自动DDL
如果订阅端表已经存在,将不同步第一次创建表的DDL,只同步后续DDL。如add/drop column。
结合上面的列级复制,可能会出现如下语法:
CREATE PUBLICATION pub FOR TABLE my_table (col1, col2) WITH (ddl='table,others');
按照上面的语法,创建了一个发布者,指定要同步my_table的列col1,col2,但因为开启了ddl自动同步的功能,所以就可能出现如下几种情况:
ddl同步完整的表结构
同步my_table的完整表结构到订阅端,这个时候订阅端的my_table的表结构和发布端完全一致。
- 方式一:由于有完整的表结构,可以选择同步所有的列数据,但此时订阅端存储空间会增长,且会对更改列级同步的语意;
- 方式二:虽然有完整的表结构,但只同步指定的列,此时没有改变列级同步的语意,但表结构里有多余的表字段;
同步完整的表结构时,当发布端表结构发生变更,比如新增列时会再次出发同步。
ddl仅同步指定的列
ddl同步的时候,仅同步my_table里的col1, col2列,dml同步的时候也仅同步对应列的数据。
需要对ddl同步做特殊处理,需要重写用户输入的ddl。
在后续发布端表结构发生表更时,比如新增列时,无需再同步DDL。
结论
对于逻辑复制列级同步时的ddl如何同步的本质是:是否允许发布端和订阅端的表结构不一致。
- 如果要保持允许发布端和订阅端表结构可以不一致,那就需要保证ddl同步的时候构造新的指定列的sql同步;
- 如果要限制逻辑同步支持DDL的场景,发布端和订阅端表结构完全一致,那就需要ddl同步时同步完整的表结构;同时为了减少数据的存储,DML可以考虑只同步指定的列。
由于当前的逻辑复制支持ddl设计实现采用把客户端的sql保存下来传到订阅端重新执行,因而无法做到对sql重写,订阅端将会是完整的表结构。同步DML数据时还是按照指定的列来同步。