暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL 15 中的逻辑复制改进

原创 小小亮 2022-11-23
1136

PostgreSQL 中有多个领域,如分区、逻辑复制、并行查询、Vacuum 等,每个新版本都会改进这些领域。在这篇博客中,我将总结用户在最近发布的PostgreSQL 15 中可以看到的逻辑复制的各种增强功能。

允许复制准备好的交易:

在上一个版本中,我们允许对准备好的事务进行逻辑解码, 而在这个版本中,我们添加了对复制准备好的事务的支持到内置逻辑复制。以前,我们仅在提交准备完成后才发送准备事务的更改。用户可以使用以下语法在 PREPARE 时间启用复制:

为所有表创建发布 mypub;

创建订阅 mysub CONNECTION 'dbname=postgres' PUBLICATION mypub WITH (two_phase = true);

此功能的主要优点是:

(a) 通过在 PREPARE 时间复制数据而不是等到 COMMIT PREPARED 来减少复制数据的延迟

(b) 这为构建无冲突逻辑复制提供了基础,因为如果准备在订阅者节点上失败,那么也可以在发布者节点上回滚它。

实施要点:

(a) 一旦所有表的初始同步完成,就可以复制准备好的事务。

(b) 为了避免在 APPLY 期间准备好的事务中发生冲突,我们使用准备标识符作为 pg_gid_<subscriber-id>_<transaction-id>。

(c) 不允许使用 ALTER SUBSCRIPTION 命令更改此选项。

(d) 一旦为订阅启用了 two_phase,就允许 ALTER SUBSCRIPTION REFRESH PUBLICATION 和 copy_data=false。


允许复制架构中的所有表:

以前,如果要发布特定模式的所有表,则需要在创建发布时指定特定模式的所有表。然后,如果稍后用户在该模式中创建了更多表,则还需要将它们单独添加到发布中。这会给用户带来不便。此功能允许用户仅指定架构名称,以防他们希望发布架构的所有表,从而使用户更加轻松。指定模式名称 (TABLES IN SCHEMA) 的语法如下:

为 SCHEMA mysch 中的表创建出版物 mypub;

CREATE PUBLICATION mypub FOR TABLE mytab, TABLES IN SCHEMA mysch;

请注意,允许使用其他模式中的单个表指定模式。用户可以使用以下语法将模式添加到现有发布中:

更改发布 mypub 在 SCHEMA mysch 中添加表;

请注意,将模式添加到已被某些订阅者订阅的发布将需要在订阅者端执行 ALTER SUBSCRIPTION … REFRESH PUBLICATION 操作才能生效。


允许为表的逻辑复制指定行过滤器:

此功能允许在发布定义中的每个表之后指定一个附加的 WHERE 子句。不满足此 WHERE 子句的行将被过滤掉。这允许部分复制一组表。行过滤器是每个表。WHERE 子句必须括在括号中。用户可以使用以下命令定义行过滤器:

CREATE PUBLICATION mypub FOR TABLE mytab1 WHERE (c1 > 10 and c2 < 20), mytab2 WHERE (c3 LIKE 'bob');

允许用户使用以下命令为发布中的现有表指定行过滤器:

ALTER PUBLICATION mypub SET TABLE mytab1 WHERE (c1 > 10 and c2 < 20), mytab2 WHERE (c3 LIKE 'bob');

这有助于在节点之间分发数据,通过有选择地发送数据和隐藏一些敏感数据来提高性能。

有关此功能的要点:

(a) 添加到发布 UPDATES 和/或 DELETES 的发布的表的行过滤器 WHERE 子句必须仅包含 REPLICA IDENTITY 涵盖的列。

(b) 添加到发布 INSERT 的发布的表的行过滤器 WHERE 子句可以使用任何列。

(c) TRUNCATE TABLE 命令忽略行过滤器。

(d) 如果行过滤器的计算结果为 NULL,则它被视为“假”,也就是不会复制相应的行。

(e) WHERE 子句只允许没有用户定义函数、用户定义运算符、用户定义类型、用户定义排序规则、非不可变内置函数或对系统列的引用的简单表达式。

(f) 在初始表同步期间,仅将满足行过滤器的数据复制到订阅者。

(g) 对于分区表,发布参数 publish_via_partition_root 决定它是使用分区的行过滤器(如果参数为 false,则默认)还是根分区表的行过滤器。


允许为表的逻辑复制指定列列表:

此功能允许在将表添加到逻辑复制时指定可选的列列表。未包含在此列表中的列不会发送到订阅者,从而允许订阅者上的模式成为发布者模式的子集。列的选择可以基于行为或性能原因。用户可以使用以下语法定义列列表:

为表 mytab1 (c1, c2), mytab2 (c3) 创建发布 mypub;

允许用户使用以下命令为发布中的现有表指定列列表:

更改发布 mypub 设置表 mytab1 (c1, c2);

有关此功能的要点:

(a) 如果发布发布了 UPDATES 和/或 DELETES,任何列列表都必须包括表的副本标识列。

(b) 如果发布仅发布 INSERT 操作,则列列表可以省略副本标识列。

(c) TRUNCATE TABLE 命令忽略列列表。

(d) 列列表只能包含简单的列引用。

(e) 如果发布还发布了 FOR TABLES IN SCHEMA,则无法指定列列表。

(f) 在初始数据同步期间,仅复制已发布的列。

(g) 对于分区表,发布参数 publish_via_partition_root 决定使用哪个列列表。如果 publish_via_partition_root 为真,则使用根分区表的列列表。否则,如果 publish_via_partition_root 为 false(默认值),则使用每个分区的列列表。


允许逻辑复制作为订阅的所有者运行:

以前,订阅的 APPLY 进程将以超级用户的权限运行,但现在使用 PostgreSQL 15,它将以订阅所有者的权限运行。因此,这将阻止逻辑复制工作者在表上执行插入、更新、删除、截断或复制命令,除非订阅所有者有权这样做。我们只允许超级用户、具有旁路器的角色和表所有者可以复制到具有行级安全策略的表中。

这项工作的目的是允许非超级用户管理订阅,并保护订阅服务器免受发布者端的恶意活动。

解决冲突:

在订户中应用事务期间,可能由于各种原因(如 PRIMARY KEY 违规、架构不同等)而发生冲突。默认情况下,PostgreSQL 将继续重试出现错误的操作。在 PostgreSQL 15 之前,用户有以下选择 (a) 他们可以手动删除冲突数据以允许复制继续进行。(b) 使用 pg_replication_origin_advance() 将 LSN 推进到失败事务之后的位置,以便在重新启动时从冲突事务之后的点开始复制。

用户使用这些方法中的任何一种都非常不方便,因为对于选项 (a),用户被迫删除/更改订阅者的数据,即使他们希望忽略来自发布者的相应数据。要使用选项 (b),用户可能需要使用 pg_waldump 或发布者端的其他工具找到失败交易的 LSN,而且来源信息并不明显,因为它是为了复制目的而在内部生成的。在使用 pg_replication_origin_advance() 时,如果用户错误地设置了错误的 LSN(无论是未来的提交还是事务之间的某些操作),那么系统可以忽略它不应该导致副本不一致的数据。

另一个问题是,系统将继续重试应用事务,即使在没有用户干预的情况下它无法成功,并且除了使用 ALTER SUBSCRIPTION mysub DISABLE 手动禁用订阅之外,用户没有任何方法可以阻止它;

在 PostgreSQL 15 中,我们试图通过提供所需信息和提供类似但更健壮的方式来简化 pg_replication_origin_advance() 的使用。它提供的另一个功能是允许在出错时禁用订阅。

引入了一个新的订阅选项“disable_on_error”,如果订阅工作人员在从发布者复制数据期间检测到任何错误,则允许自动禁用订阅。用户可以在 CREATE SUBSCRIPTION 期间或在 ALTER SUBSCRIPTION 命令中指定此选项。

创建订阅 mysub CONNECTION '...' PUBLICATION mypub WITH (disable_on_error = true);

更改订阅 mysub 设置(disable_on_error = true);

然后,我们通过添加 (a) Finish LSN 来扩展订阅工作者错误的错误上下文信息。它将为提交的事务指示 commit_lsn,为准备好的事务指示 prepare_lsn。(b) 复制源名称。这将包含复制源的名称,该复制源跟踪复制进度并使用订阅定义自动创建。

扩展的错误上下文信息可以使用户更容易使用 pg_replication_origin_advance()。

然后,我们还介绍了一种更健壮的方法来跳过冲突事务,使用命令:ALTER SUBSCRIPTION mysub SKIP (lsn = '0/1566D10'); 这更健壮,因为它将防止用户通过对指定的 LSN 执行检查来设置一些错误的 LSN。我们确实确保指定的 LSN 必须与重启后发布者发送的第一个事务的完成 LSN 相同。成功应用于订阅者的第一个事务将清除指定的 LSN。我们还确保指定的 LSN 必须大于源的当前 LSN。


pg_stat_subscription_stats:

一个新视图,显示有关在应用逻辑复制更改期间或在初始表同步期间发生的错误的统计信息。

发布者和订阅者之间的通信改进:

PostgreSQL 在通信方面进行了增强,以 (a) 防止发送事务 BEGIN/END 消息,其中所有事务数据都被过滤,以及 (b) 防止复制在处理大部分或所有数据被过滤的大型事务时由于超时而重新启动。

在 PostgreSQL 15 之前,我们使用为空事务发送 BEGIN/END 消息(其中所有更改都被跳过/过滤),这会浪费大量 CPU 周期和网络带宽来构建和传输此类消息。为了避免为空事务发送消息,我们开始仅在发布者向订阅者传输第一个更改时才发送 BEGIN 消息,然后我们允许仅在发送 BEGIN 时发送 END(COMMIT)消息。为了避免同步复制中的任何延迟,我们确实会在跳过空事务并处理其反馈后发送一条保活消息。

在处理由于特定操作未发布而过滤掉大部分更改的长事务时,发布者不会向订阅者发送任何通信,订阅者会在特定阈值时间后超时导致复制重新启动。为了解决这个问题,我们开始在这种情况下定期发送 keep_alive 消息。


我相信这是 PostgreSQL 15 中逻辑复制改进的一个很好的组合,它将帮助用户。欢迎您在这里或在 PostgreSQL 邮件列表上提供反馈!


原文标题:Logical Replication Improvements in PostgreSQL-15

原文作者:Amit Kapila
原文链接:http://amitkapila16.blogspot.com/2022/11/logical-replication-improvements-in.html

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论