逻辑复制是一种基于复制标识(通常是主键)复制数据对象及其变更的方法。逻辑复制与物理复制相反,物理复制使用精确的块地址和逐字节复制。了解更多关于PostgreSQL中的逻辑复制,请参阅pgsql-docs。
1、已实现的功能
逻辑解码
逻辑复制奠基于2014-12-18发布PostgreSQL-9.4中,引入了逻辑解码,即允许数据库变更以可定制的格式流出,复制插槽,允许保留主库上的资源,如WAL文件,直到备库不再需要它们,并引入了EQUIPMENT Identity(控制逻辑复制操作的表级选项)。
通过原点跟踪复制进度
在PostgreSQL-9.5中,引入了一些特性,比如通过原点跟踪复制进度,允许将来解决冲突的提交时间戳。复制起点允许从重新启动之前的点重新启动复制。复制起点还可以帮助避免复制设置中的双向循环,这是PostgreSQL 16版本中引入的功能。
逻辑解码的通用WAL消息的支持
在PostgreSQL-9.6中,增加了对用于逻辑解码的通用WAL消息的支持。允许扩展将数据插入到逻辑解码插件可以读取的WAL流中。
发布/订阅模式的本地逻辑复制
在PostgreSQL-10.0中,引入了使用发布/订阅模型的本地逻辑复制。这允许比物理复制更大的灵活性,包括在PostgreSQL的不同主要版本之间的复制和选择性复制,在此版本中,还允许复制表上的插入/更新/删除操作。从现在开始,在每个版本中都增强了此功能。
复制Truncate操作
在PostgreSQL-11.0中,允许复制Truncate操作。逻辑解码开始使用分代内存分配器,该分配器针对串行分配/释放进行了优化,并减少了解码的内存使用。
复制逻辑复制插槽
在PostgreSQL-12.0中,允许复制逻辑复制插槽。在调查逻辑复制问题时,此功能很有帮助;并选择不同的输出插件来使用变更。
分区表的复制
在PostgreSQL-13.0中,引入了分区表的复制,允许在需要复制各个分区之前复制其所有分区。然后允许通过GUClogical_decoding_work_mem控制用于逻辑解码的内存量,更多请阅读博客。然后,还允许复制插槽的WAL存储受GUCmax_slot_wal_keep_size的限制。
In-Progress事务的逻辑复制
在PostgreSQL-14.0中,引入In-Progress事务的流,这减少了大型事务的应用延迟。然后允许对准备好的事务进行解码,这意味着输出插件可以在准备时使用变更,而不是等待提交,这有助于减少延迟。然后,还减少了CPU的使用,并提高了具有大量DDL的事务的解码性能。14.0中引入了一长串功能,您可以阅读博客了解更多细节。
复制为核心逻辑复制准备的事务
在PostgreSQL-15.0中,允许复制为核心逻辑复制准备的事务,除了减少应用延迟外,这还构成了构建无冲突逻辑复制的基础。然后,用户可以使用行筛选器和列列表来发送选择性的表数据。还允许发布Schema中的所有表。您可以阅读blog以了解有关15.0中引入的逻辑复制功能的更多信息。
防止双向复制中的循环
PostgreSQL-16.0版本中引入的特性。,可以设置双向复制,但它有一些问题,比如可能会发生冲突,这样的设置可能会导致双向循环。这个特征旨在解决第二个问题。让我分享一个简单的例子来说明循环是如何发生的。
#Publisher
CREATE TABLE mytbl(c1 int primary key);
CREATE PUBLICATION mypub FOR TABLE mytbl;
#Subscriber
CREATE TABLE mytbl(c1 int primary key);
CREATE SUBSCRIPTION mysub CONNECTION 'dbname=postgres' PUBLICATION mypub;
CREATE PUBLICATION mypub FOR TABLE mytbl;
#Publisher
CREATE SUBSCRIPTION mysub CONNECTION 'dbname=postgres port=5444' PUBLICATION mypub;
INSERT INTO t1 values(1);
#Publisher's server LOG:
ERROR: duplicate key value violates unique constraint "t1_pkey"
DETAIL: Key (c1)=(1) already exists.
这个错误是因为订阅者节点在应用变更后将其发送回发布者。现在,如果mytbl上没有定义主键,将再次在发布者上插入相同的行,并再次将其发送给订阅者。因此,这将导致发布者和订阅者之间的无限循环。
引入了原点过滤,避免了这样的问题。需要在上述示例中的发布者上执行以下语句以避免循环。
ALTER SUBSCRIPTION mysub SET(origin=none);
以下是使用此功能:的语法:
CREATE SUBSCRIPTION sub1 CONNECTION ... PUBLICATION pub1 WITH (origin = none);
有效值为“none”和“any”,默认值为“later”。
将来源设置为“none”,意味着订阅将请求发布者只发送没有来源的变更。
将来源设置为“any”意味着发布者发送变更而不考虑其来源。
更多信息,阅读[这里](https://www.postgresql.fastware.com/blog/bi-directional-replication-using-origin-filtering-in-postgresql)
(https://www.crunchydata.com/blog/active-active-postgres-16.)
允许从备库执行逻辑解码
这需要在主库和备库上都设置wal_level = logical。让看一个例子:
postgres=# select pg_is_in_recovery();
pg_is_in_recovery
\-------------------
t
(1 row)
上面是为了说明下面的语句是在待机状态下执行的。
postgres=# select * from pg_create_logical_replication_slot('slot_1', 'test_decoding', false, false);
slot_name | lsn
-----------+-----------
slot_1 | 0/50001A0
(1 row)
postgres=# SELECT * FROM pg_logical_slot_get_changes('slot_1', NULL, NULL);
lsn | xid | data
-----------+-----+----------------------------------------
0/5000250 | 734 | BEGIN 734
0/5000250 | 734 | table public.t1: INSERT: c1[integer]:3
0/5000348 | 734 | COMMIT 734
(3 rows)
通过允许订阅者在主库忙碌时从备库订阅,这种能力可以用于工作负载分配。需要确保当所需的数据在主数据库中被删除时,不能从插槽中解码。因此,在以下情况下使备库上的逻辑插槽无效:
(a)在主库上删除了所需的行,以及(b)在主库上的wal_level降低到逻辑以下。可以检查pg_replication_slots中的“conflicting”字段,以了解插槽是否由于冲突而无效。
在表所有者的权限下执行操作
可以将应用进程配置为使用表所有者的权限,而不是订阅所有者的权限执行操作。它可以使用“run_as_owner”选项配置为“false”,订阅所有者需要能够对拥有复制表的每个角色设置ROLE。如果表所有者没有权限对订阅设置ROLE,则会强制使用SECURITY_RESTRICTED_OPERATION。如果订阅已配置为run_as_own =true,则不会发生用户切换。这也意味着,任何拥有正在进行复制的表的用户都可以使用订阅所有者的权限执行任意代码。
非超级用户可以创建订阅
为此,非超级用户必须已被授予pg_create_subscription角色,并且需要指定身份验证密码。非超级用户还必须对要在其中创建订阅的数据库具有CREATE权限。
大型事务并行应用
每个大事务都被分配给一个可用的工作者,这通过立即应用而不是等待整个事务被订阅者接收来改善延迟。在事务完成之前,工作线程一直保持被分配状态。这在大多数情况下保留了提交顺序并避免了文件I/O,尽管如果没有可用的worker,仍然需要溢出到一个文件。
维护提交顺序是很重要的,避免由于以下原因导致的失败:(a)事务依赖性:例如:如果在第一个事务中插入一行,并在发布者上的第二个事务中更新它,那么允许订阅者并行应用这两个事务可能会导致更新失败;(b)死锁:允许以相反顺序更新同一组行/表的事务被并行应用可能导致死锁。
逻辑复制可以复制二进制格式的表
根据列类型的不同,二进制格式的数据表可能会减少所花费的时间。在V16之前,此选项仅允许复制以二进制格式复制的表。只有当发布者和订阅者都是v16或更高版本时,才支持二进制副本。
可在订阅服务器上使用PK和REPLICA IDENTITY以外的索引
当REPLICA IDENTITY或PK索引不可用时,在发布服务器上使用REPLICA IDENTITY FULL会导致对订阅服务器上的每个元组变更进行全表扫描。此功能允许考虑其他索引。
可以使用的索引必须是btree索引,而不是部分索引,最左边的字段必须是引用远程关系列的列(而不是表达式)。这些限制有助于保持索引扫描与PK/RI索引扫描相似。
性能的提高与表中的数据量成正比。
在线升级逻辑和物理复制节点
过去,在主要版本通过pg_upgrade升级之后,用户无法立即连接并将数据写入逻辑复制节点。因为在升级过程中丢失了插槽,导致复制无法继续。因此,新的写入将无法复制,导致两个节点上的数据变得不同步。正如这篇博客文章中所解释的,用户必须阻止应用程序写入,直到升级后重新启用复制设置。
PostgreSQL 17可以升级逻辑复制节点,而无需阻止写入或要求用户手动分离/附加订阅或创建插槽。仅当旧群集为17.0或更高版本时才支持逻辑插槽迁移,因此用户在从17.0升级到18.0(或更高版本)时将受益于此功能。有关升级发布者和订阅者节点的先决条件,请参阅PostgreSQL文档。本功能详情请参阅这里
同步复制插槽以允许故障切换
同步复制插槽以允许故障切换将执行从发布服务器到物理备库的逻辑插槽同步。如果发布者停机,可以提升其物理备库,然后订阅者可以连接到它并工作。
物理备库上的插槽同步的工作方式是:从主库获取逻辑故障转移插槽信息,在备库上创建插槽,并定期对其进行同步。这可以通过启用插槽同步工作进程自动完成,也可以使用pg_sync_replication_slots()函数手动完成。
本功能详情请参阅[这里]
2、正在开发或讨论的功能
截至2024年10月,正在开发或讨论的未来功能主要有:
DDL复制
DDL复制是允许无缝主版本升级的最常见功能之一,当需要复制所有用户表时,当需要复制某个模式中的一部分表,且这些表与其他模式没有外键依赖时,需要DDL复制支持。
pgl_ddl_deploy扩展:可实现透明的逻辑DDL复制,pgl_ddl_deploy支持pglogical和原生逻辑复制,自动处理DDL事件,无需手动干预;支持多种配置选项,如正则表达式过滤、锁安全模式等,并提供详细的日志和监控功能。具体如:支持通过逻辑复制技术(包括pglogical和原生逻辑复制)自动传播DDL更改(如创建表、修改表结构等)。支持在创建表时自动将其加入逻辑复制。可根据schema或表的正则表达式进行过滤,选择性地复制DDL。提供选项在订阅者上以锁安全的方式部署DDL更改。支持将失败的DDL操作排队并稍后重试,避免阻塞复制。
当前,该扩展不支持涉及多个表的DDL操作。如CREATE TABLE AS和SELECT INTO。对于多语句客户端SQL的限制。详见:https://github.com/enova/pgl_ddl_deploy
序列复制
尽管,詹姆斯·阿姆斯 在2023年3月8日《使用pglogical的双向复制》谈到pglogical处理与表分开的序列。除了表之外,还需要向复制集中添加序列。可通过一次添加所有序列来完成:
SELECT pglogical.replication_set_add_all_sequences('default', ARRAY['public']);
当新节点订阅复制集时,将同步序列并为每个节点创建偏移量。这样就不会有重叠了然后定期重新同步序列。详细见这里
以上为序列复制问题的解决提供了参考,但在双(多)架构及复杂场景下的序列复制的实现还需要深入研究、开发和实践。
3、未来需要考虑解决的问题
此外,PostgreSQL逻辑复制需要考虑但不限于以下问题:
- 多主环境内建的冲突检测与自动解决机制。
- 大规模数据同步时分区表复制仍有性能和一致性。
- 复杂的多主逻辑复制复制。
- 异构数据库之间的数据同步。
- 跨地理位置的复制高延迟。
- 在多节点环境中,逻辑复制自动化的故障转移和恢复机制。
4、参考文献
- https://www.postgresql.org/docs/current/logical-replication.html
- https://amitkapila16.blogspot.com/2023/09/
- https://www.enterprisedb.com/postgres-tutorials/postgres-13-logicaldecodingworkmem-and-how-it-saves-your-server-going-out-memory
- https://amitkapila16.blogspot.com/2021/07/logical-replication-of-in-progress.html
- https://amitkapila16.blogspot.com/2024/09/online-upgrading-logical-and-physical.html
- https://amitkapila16.blogspot.com/2024/10/failover-slots-in-postgresql-17.html
- https://www.jamesarmes.com/2023/03/bidirectional-replication-postgresql-pglogical.html
- https://github.com/enova/pgl_ddl_deploy




