在PostgreSQL中使用逻辑复制时,逻辑复制插槽的故障切换一直是难点。这种功能的缺乏破坏了逻辑复制的使用,并成为最大的威慑因素之一。其利害关系和影响如此之大,以至于许多组织不得不放弃围绕逻辑复制的计划,这影响了许多迁移到PostgreSQL的计划。看到许多人不得不选择专有/特定于供应商的解决方案,这让人感到痛苦。
在Percona,我们过去曾写过这方面的文章:缺少的部分:逻辑复制插槽的故障切换.在那篇文章中,我们讨论了解决此问题的一种可能方法,但没有可靠的机制将插槽信息复制到物理备份并对其进行维护。
简而言之:复制插槽将始终保持在主节点上。如果有一个switchover/failover升级了其中一个备用节点,那么新的主节点将不知道前一个主节点维护的复制插槽。这会中断下游系统的逻辑复制,或者如果创建了一个新插槽,则使用起来就不安全。
好消息是,Patenti开发人员和维护人员从2.1.0版开始解决了这个问题,并提供了一个有效的解决方案,没有任何侵入性的方法/扩展。对我来说,这是一部值得Patroni社区热烈鼓掌的作品,这也是这篇博文的意图,也是为了确保更多的人知道这一点。
如何设置
Percona repository提供现成的Patroni包。但你可以从任何渠道自由使用Patroni。
基本配置
如果您对此感到兴奋并想尝试,以下步骤可能会有所帮助。
整个讨论都是关于逻辑复制的。因此,最低要求是将wal_level 设置为“logical”。如果现有的用户配置将wal_level 设置为“replica”,并且如果您想使用此功能,您可以只编辑Patroni配置。
但是,此更改需要重新启动PostgreSQL:
最后一列状态栏 Pending restart
里面都是 *
号,表示配置已经修改,需要重启生效。
您可以使用Patroni的switchover
功能重新启动节点,使更改生效,因为降级的节点需要重新启动。
如果还有剩余的节点,可以稍后重新启动。
创建逻辑槽
现在我们可以为PostgreSQL添加一个永久的逻辑复制槽,该槽将由Patroni维护。
编辑Patroni配置:
$ patronictl -c etc/patroni/patroni.yml edit-config
可以按如下方式添加槽:
…
slots:
logicreplia:
database: postgres
plugin: pgoutput
type: logical
…
“slots:”部分定义了永久复制槽。这些槽将在switchover/failover切换期间保留。pgoutput
是PostgreSQL逻辑复制的解码插件。
一旦应用了更改,将在主节点上创建逻辑复制槽。可以通过查询来验证:
select * from pg_replication_slots;
以下是一个示例输出:
同样的复制槽也将在备节点上创建。Patroni 在内部将复制槽信息从主节点复制到所有符合条件的备节点。
我们可以在备节点的pg_replication_slot上使用相同的查询,并查看类似信息
下面是一个示例,显示了同一复制槽在备节点端的反馈:
通过在创建订阅端时显式指定插槽名称,订阅端可以使用此插槽。
CREATE SUBSCRIPTION sub2 CONNECTION ' PUBLICATION WITH (copy_data = true, create_slot=false, enabled=true, slot_name=logicreplia);
或者,可以修改现有订阅端指定新复制槽。
例如:
ALTER SUBSCRIPTION name SET (slot_name=logicreplia);
相应的PostgreSQL日志条目可以确认复制槽名称的更改:
2021-12-27 15:56:58.294 UTC [20319] LOG: logical replication apply worker for subscription "sub2" will restart because the replication slot name was changed
2021-12-27 15:56:58.304 UTC [20353] LOG: logical replication apply worker for subscription "sub2" has started
在发布端,我们可以通过检查active_pid和推进插槽的LSN来确认插槽使用情况。
随着逻辑复制从主节点进行,Patroni集群的所有备节点中的复制槽信息也会更新
在更高的层次上,这正是这个特性所做的:
自动创建/复制 槽信息,将其从Patroni集群中的主节点复制到所有符合条件的备用节点。
随着LSN在主节点上相应槽上的前进,备节点槽上的LSN也自动前进。
Switchover/Failover 后
在switchover/Failover的情况下,我们不会丢失任何槽信息,因为它们已经在备节点上维护。
切换后,拓扑结构如下所示:
现在,任何下游逻辑副本都可以重新指向新的主节点。
postgres=# ALTER SUBSCRIPTION sub2 CONNECTION 'host=192.168.50.10 port=5432 dbname=postgres user=postgres password=vagrant'; ALTER SUBSCRIPTION
复制将继续,pg_replication_slot信息可以证实这一点。
总结+要点
逻辑复制槽在概念上只能在主实例上使用,因为逻辑解码发生在主实例上。现在,有了这一改进,Patroni可以确保槽信息在standby状态下也可用,并且可以从订阅端那里接管连接。
此解决方案需要PostgreSQL 11或更高版本,因为它使用pg_replication_slot_advance()函数来推进槽,该函数从PostgreSQL 11开始提供。
下游连接可以使用HAProxy,这样连接将自动路由到主节点(本文未涉及)。不需要修改PostgreSQL代码或创建任何扩展。
槽的复制是通过PostgreSQL协议(libpq)进行的,而不是任何特定于操作系统的工具/方法。Patroni 使用rewind
或superuser
凭证。Patroni 使用pg_read_binary_file()函数读取槽信息。源代码参考
在备节点创建逻辑槽后,Patroni使用pg_replication_slot_advance()向前更新槽。
永久槽信息将添加到DCS,并由Patroni的主实例持续维护。引入了一个名为“status”的新DCS密钥,并支持所有DCS选项 (zookeeper, etcd, consul, etc.)。
必须在需要维护逻辑复制槽的所有备节点上启用hot_standby_feedback。
必须启用Patroni参数postgresql.use_slots,以确保每个备节点使用主节点上的槽。