内容提要
文章介绍用Spock多主逻辑复制实现PostgreSQL零停机大版本升级:逐节点新建新版本节点,用Zodan的add_node加入集群并同步数据,验证双向复制后迁移流量,再用remove_node下线旧节点。需注意关闭DDL复制时新表未加入复制集、Spock槽需设failover、Patroni会覆盖Spock配置等问题。
延伸解读
零停机升级的关键:多主逻辑复制
文章利用Spock多主逻辑复制,让每个节点都能接受写入,从而避免传统pg_upgrade需要停机的痛点。升级时逐个添加新版本节点,迁移流量后再下线旧节点,应用始终有可写端点。但需注意,这并非完全无中断:关闭DDL复制期间不能进行模式变更,且新节点加入后必须手动复制复制集成员,否则写入不会传播。
易被忽略的配置陷阱
作者在测试中遇到三个隐蔽问题:关闭DDL复制后,新节点的表未加入复制集,导致单向复制且状态检查仍显示健康;Spock槽默认failover=false,需手动改为true才能参与槽同步;Patroni会覆盖Spock所需参数,必须将wal_level、shared_preload_libraries等写入etcd配置。这些陷阱不会在常规状态检查中暴露,需通过双向写入测试验证。
备节点与故障转移的权衡
旧节点的物理备节点不升级,而是从新版本节点用pg_basebackup重建,由Patroni自动完成。为保障故障转移后复制不丢失,需将Spock槽标记为failover,并设置synchronized_standby_slots,使主节点等待备节点收到WAL后再发送给其他节点。代价是备节点宕机时,主节点会暂停向其他Spock节点发送变更,直到备节点恢复。
Q&A
如何在不中断应用的情况下对分布式PostgreSQL集群进行大版本升级?
使用Spock多主逻辑复制,逐节点添加新版本节点,通过Zodan的add_node加入集群并同步数据,验证双向复制后迁移流量,再用remove_node下线旧节点。整个过程应用始终有可写节点。
Spock的add_node在升级过程中具体做了什么?
add_node会从现有节点复制模式和数据到新节点,并在所有节点之间建立双向订阅,同时集群继续接受写入。它还会检查Spock版本和角色是否匹配。
升级时关闭DDL复制后,新节点上的表为什么不会自动加入复制集?
因为DDL复制关闭后,新节点上创建的表不会自动加入复制集,导致新节点只接收变更但不发布任何变更。需要手动从源节点复制复制集成员关系,使用spock.repset_add_table添加表。
新节点上的Spock复制槽需要设置failover吗?为什么?
需要。add_node创建的复制槽默认failover=false,原生槽同步会跳过这些槽,导致故障转移后其他节点丢失复制位置。必须在暂停订阅的情况下将槽的failover改为true。
Patroni会覆盖Spock的配置吗?如何避免?
会。Patroni接管节点后会用自身默认值替换Spock所需配置(如shared_preload_libraries、wal_level等)。必须将所有Spock要求添加到Patroni的etcd集群配置中,然后重启节点。
升级后如何下线旧节点?
在旧节点上运行zodremove.sql并调用spock.remove_node,它会删除该节点的订阅、复制集和集群注册信息。之后确认剩余节点不再列出该节点且无残留复制槽。