Patroni中对主备切换、故障转移和命令行的流程整理
1. 主备切换
主备切换(Switchover)是高可用性(High Availability,HA)系统中的一个重要操作,常见于数据库集群或分布式系统中。在主备架构中,通常有一个主节点和一个或多个备节点,备节点会复制主节点的数据并保持同步。
主备切换通常有两种操作模式:手动主备切换和自动主备切换,具体取决于故障情况或系统配置。
1.1 手动
1.1.1 介绍
手动主备切换是由管理员主动执行的操作,通常用于非故障情况下的维护、升级或负载均衡。
场景:
- 维护和升级:当管理员需要对主节点进行维护或升级时,可以手动将备节点提升为主节点。
- 负载均衡:当主节点负载过高时,管理员可以主动进行主备切换,将负载均衡到其他节点。
- 灾难恢复演练:在测试或演练灾难恢复流程时,也需要手动触发主备切换。
操作流程:
- 监控状态:首先确认当前主节点的健康状态,确保备节点已同步。
- 切换操作:管理员通过集群管理工具(如
Patroni
、pg_ctl
、systemctl
等)手动发起主备切换。 - 验证切换:验证新的主节点是否正常工作,确认系统切换成功。
优点:
- 控制性强:管理员可以灵活选择切换时机。
- 低风险:在非故障情况下进行切换,可以避免不必要的风险。
缺点:
- 需要人工干预,可能会出现操作延迟或人为失误。
1.1.2 流程解析
1.1.2.1 请求发送和解析
手动进行主备切换即使用switchover
命令。
patronictl -c /usr/local/patroni/patroni_pg.yml switchover demo
patronictl -c /usr/local/patroni/patroni_pg.yml switchover demo --group 0
进入ctl.py
文件的switchover
函数,其中调用_do_failover_or_switchover
函数来实现故障转移和主备切换。在其中整理当前主节点信息、要切换的候选节点信息和调度时间等信息,再向 REST API 发送请求,会发送/switchover
或者/failover
请求路径。在api.py
中发现两者都是使用do_POST_failover
函数来处理这两个请求。
发送请求的代码如下:
member = cluster.leader.member if cluster.leader else candidate and cluster.get_member(candidate, False)if TYPE_CHECKING: # pragma: no coverassert isinstance(member, Member)r = request_patroni(member, 'post', action, failover_value)# probably old patroni, which doesn't support switchover yetif r.status == 501 and action == 'switchover' and b'Server does not support this operation' in r.data:r = request_patroni(member, 'post', 'failover', failover_value)
在do_POST_failover
函数会对switchover
或failover
请求解析请求体的信息,然后做一系列的验证,如果验证都通过,则根据请求体中提供的信息执行手动的主备切换或者故障转移操作,并且注册到 DCS 中。其中调用 DCS 中的manual_failover
将操作信息注册到 DCS 中。在is_failover_possible
函数中进行检查故障转移或主备切换操作的状态,直到操作完成或超时,返回相应的状态信息。
将操作注册到 DCS 的代码如下:
if self.server.patroni.dcs.manual_failover(leader, candidate, scheduled_at=scheduled_at):self.server.patroni.ha.wakeup()if scheduled_at:data = action.title() + ' scheduled'status_code = 202else:status_code, data = self.poll_failover_result(cluster.leader and cluster.leader.name,candidate, action)
主备切换的请求发送流程图如下:

1.1.2.2 ha循环中切换
在发送和解析完请求之后,会将信息注册到 DCS 中,在patroni
开启的主循环中,使用ha
类的_run_cycle
循环函数遍历节点信息,来进行切换操作。
在_run_cycle
函数中会对各种条件进行判断从而判断当前节点应该处于什么操作中,在这个函数中可以对新节点进行初始,也会让当前用于领导者锁的故障节点释放领导者锁从而触发一个新的主节点提升,从而完成故障转移的流程,并且在这个函数中也调用了recover
函数来恢复 pg 实例。在其中也有一段对集群状态的判断和切换,这代应该是对不健康集群进行主备切换或故障转移的部分。代码如下:
if self.cluster.is_unlocked():ret = self.process_unhealthy_cluster()
else:msg = self.process_healthy_cluster()ret = self.evaluate_scheduled_restart() or msg
并且在主备切换或故障转移中对复制槽进行复制。代码如下:
is_promoting = self._async_executor.scheduled_action == 'promote'if (not self._async_executor.busy or is_promoting) and not self.state_handler.is_starting():create_slots = self._sync_replication_slots(False)if not self.state_handler.cb_called:if not is_promoting and not self.state_handler.is_primary():self._rewind.trigger_check_diverged_lsn()self.state_handler.call_nowait(CallbackAction.ON_START)if not is_promoting and create_slots and self.cluster.leader:err = self._async_executor.try_run_async('copy_logical_slots',self.state_handler.slots_handler.copy_logical_slots,args=(self.cluster, self.patroni, create_slots))
其中重点实现的函数为process_unhealthy_cluster
和process_healthy_cluster
。这也是实现自动主备切换和故障转移的函数。
ha主循环的流程图如下:

process_unhealthy_cluster
处理不健康的集群所调用的函数,主要进行故障转移的操作。通过判断当前节点是否是最健康的节点,来判断是否来进行故障转移,然后获取主节点锁,尝试将当前节点提升为主节点(调用enforce_primary_role
函数),如果无法提升就会降级为备节点跟随其他节点。
enforce_primary_role
函数则是当一个节点赢得领导者选举,满足提升为主节点的条件时执行相关的操作。最终调用 pg 的promote
函数提升为主节点。
下图是process_unhealthy_cluster
函数的大致流程图。

process_healthy_cluster
处理健康汲取你所调用的函数,主要进行角色的变更处理,可以进行主备切换或故障转移。根据集群的健康程度和锁的状态来决定是否需要进行角色变更(提升为主节点、降级为备节点)。其中重要的函数处理即为process_manual_failover_from_leader
函数,来处理普通的故障转移和主备切换。
下图是process_healthy_cluster
函数的大致流程图。

1.1.2.3 流程分析
经过代码分析整理,后使用不含mpp处理器的配置文件启动patroni,部署一个一主一从的集群,然后执行手动主备切换,发现会经过以下流程:
-
发送
switchover
请求:将请求体内容写入 DCS 中[zk: localhost:2181(CONNECTED) 2] get /pgsql/demo/failover {"leader":"pgsql1","member":"pgsql2"}
如果在switchover命令行中没有对Primary、Candidate指定而采用默认值的话,不会有member,只会有pgsql1。
并且这种情况会调用
process_unhealthy_cluster
函数来处理switchover请求,会当成一个故障转移来处理。 -
如果
pgsql1
要从主库转换为备库,在process_healthy_cluster
函数中对pgsql1
进行降级。调用process_manual_failover_from_leader
函数。下面则是调用process_manual_failover_from_leader
函数返回的消息,表明原主pgsql1
将会降级自己。'switchover: demoting myself'
如果是
pgsql1
从备库转换为主库,则会在process_healthy_cluster
中进行提升操作。会调用其中的enforce_primary_role
函数将pgsql1
提升为主库,然后在_run_cycle
函数中同步复制槽信息,之后会在开启的线程中去执行 pg 中的promote
函数使用pg_ctl promote
命令提升。 -
调用 ha 中的
demote
函数,对pgsql1
进行降级操作,在这个函数中先会停止 pg 数据库,调用 pg 中的stop
函数来停止pgsql1
的数据库。然后将数据库设置为demoted
调用 pg 中的follow
函数将pgsql1
转换为备库。在这个函数中将会调用start
函数来启动数据库实例并将数据库角色设置为replica
。 -
最后在
process_healthy_cluster
或者是demote
函数中,让降级之后的主跟随现在集群的主节点即可。
总结:
- 故障转移和主备切换有些类似,如果在
patronictl switchover
中没有指定候选节点的话,就会导致集群的领导节点释放领导者锁之后需要进入process_unhealthy_cluster
函数找到最健康的节点提升为主节点(因为此时的集群中leader_name
会变为None)。这个主备切换也会被转换为一个特殊的故障转移来处理。 - 如果指定了候选节点,那么会向当前的主节点发送请求,然后主节点发生降级动作,是否领导者锁,而候选节点则得到领导者锁,并且
leader_name
变更为候选节点,此时进入process_healthy_cluster
对备节点进行提升,然后让降级后的原主跟随提升的现主即可完成一次主备切换过程。 - 在这个过程调用的各种前后处理函数,可以让数据库在主备切换的时候也让mpp处理器进行切换。
流程整理后的手动主备切换的流程图如下:

1.2 定时切换
在switchover
命令中指定scheduled
参数即可。
查看请求参数:
{'candidate': 'pgsql2', 'leader': 'pgsql1', 'scheduled_at': '2024-11-23T23:20:00+08:00'}
DCS 中存储的failover
值:
{"leader":"pgsql1","member":"pgsql2","scheduled_at":"2024-11-23T23:20:00+08:00"}
这个scheduled_at
值在process_healthy_cluster
函数中调用process_manual_failover_from_leader
来解析
if (failover.scheduled_at and notself.should_run_scheduled_action(bare_action, failover.scheduled_at, lambda:self.dcs.manual_failover('', '', version=failover.version))):return
在should_run_scheduled_action
函数中会计算出预定时间和现在时间的差值,如果十分接近就会睡眠后执行switchover
,如果较远就会继续执行主循环而不执行switchover
,如果已经过去一段时间,就会清理掉 DCS 中failover
中的旧数据。
下面是定时主备切换的执行流程图:

2. 故障转移
2.1 手动
2.1 介绍
手动故障转移是指管理员手动触发故障转移过程。在这种模式下,管理员能够根据当前的系统状况决定何时进行故障转移以及选择哪个节点作为新的主节点。
手动故障转移的流程:
- 管理员介入:管理员通过命令行工具或管理接口触发故障转移。例如,使用
patronictl
命令或 API 手动切换主节点。 - 选择新主节点:管理员选择哪个备用节点应该成为新的主节点。通常,这个选择是基于节点的健康状况、同步状态和数据一致性来决定的。
- 故障转移执行:在管理员的控制下,Patroni 会执行角色切换,并将选定的备用节点升级为新的主节点。
- 恢复操作:类似于自动故障转移,手动故障转移后也可以对故障节点进行恢复操作,使其重新加入集群。
手动故障转移的优点:
- 完全控制:管理员可以完全控制故障转移的时机和选定的节点。这对于确保数据一致性和最小化潜在风险是很重要的。
- 防止错误的自动切换:如果自动故障转移触发时的情况不理想(例如,主节点不是完全宕机,只是短暂的网络故障),手动故障转移可以防止不必要的切换。
- 预防数据丢失:管理员可以在进行故障转移前检查集群状态,确保数据已经充分同步,从而避免数据丢失。
手动故障转移的缺点:
- 需要人工干预:手动故障转移需要人工介入,可能会导致故障恢复的时间延迟,特别是在管理员不在现场时。
- 更高的操作风险:如果管理员不小心选择了错误的节点,可能会导致数据不一致或其他问题。
2.1.2 流程解析
手动进行主备切换即使用failover
命令。
patronictl -c /usr/local/patroni/patroni_pg.yml failover demo
patronictl -c /usr/local/patroni/patroni_pg.yml failover demo --group 3
手动failover
的过程与手动switchover
是的大部分的内容是相同的,即流程基本上差不多。主动故障转移是因为主节点故障,但是从节点服务转换为主节点导致需要手动切换,此时需要选择一个候选节点提升为主节点。手动故障也会发送一个failover
请求到api
,解析这个请求体也会讲请求体的信息放入DCS中,如果是自动则不会将failover
的信息放入DCS。
具体的failover
请求的发送和解析请查看1.1.1节。手动故障转移的流程图如下所示:

2.2 自动
2.2.1 介绍
自动故障转移的流程:
- 故障检测:Patroni 会周期性地通过 H/A(High Availability)检测机制 监控主节点的健康状态。如果主节点无法响应,或者无法提供写操作,Patroni 会认为主节点已经宕机。
- 选举新主节点:当主节点故障后,Patroni 会选择一个健康的备用节点(Replica)作为新的主节点。这个选择的过程是通过集群中的节点达成共识(通常基于领导者选举机制),并根据某些策略(例如,节点的时间线、数据的同步状态等)来决定最合适的候选节点。
- 角色切换:一旦选举出新的主节点,Patroni 会自动更新节点角色,并通知其他节点进行同步,使其跟随新主节点。
- 恢复模式:在故障转移完成后,Patroni 会尝试恢复故障节点(如果可能的话),并将其重新加入集群。这个过程通常是通过回滚(rewind)或者重新初始化数据库来完成。
自动故障转移的优点:
- 无人工干预:自动故障转移是完全自动化的,适合需要高可用性、最小化人工干预的场景。
- 快速恢复:因为自动化完成故障转移,通常能够迅速恢复集群的高可用性。
- 配置灵活:可以通过配置参数来调整自动故障转移的策略和容忍度。
自动故障转移的缺点:
- 潜在的数据丢失:如果集群中的数据同步策略没有配置得当(如
synchronous replication
),自动故障转移可能会导致某些数据丢失,特别是在主节点宕机后没有足够的时间进行数据同步的情况下。 - 无法预知的选举行为:在某些情况下,自动故障转移的选举过程可能不是理想的,导致不适当的节点被选为新的主节点。
2.2.2 流程解析
将集群中的主节点的patroni
进程关闭,即可触发自动故障转移。
故障转移也依赖于1.1.2 节中介绍的process_unhealthy_cluster
和process_healthy_cluster
函数。当主节点关闭时,对应的变化会被 watchdog或DCS(zookeeper) 监控到主节点失效,因此导致集群成为不健康集群,在process_unhealthy_cluster
函数中选举出最健康的节点,如果可以提升该节点,就调用promote
函数将该节点提升为主节点。提升成为主节点之后才会同步复制槽。
当主节点正常关闭时,会执行ha中的shutdown
函数来关闭数据库,在这个函数中会对能否进行故障转移进行检查,如果可以在关闭主节点的数据库之后就会释放主节点锁,但是不会显示调用demote
函数对主节点进行降级。在原主节点启动之后,在 ha 的主循环中会调用recover
函数对原主进行恢复,然后在其中发现原主并没有持有主节点锁了因此会把其作为一个备节点,异步调用 pg 的follow
函数跟随现在的主节点(修改原主节点的配置文件)。
if self._async_executor.try_run_async('restarting after failure', self.state_handler.follow,args=(node_to_follow, role, timeout)) is None:self.recovering = True
自动故障转移的流程图如下所示:

3. 命令行
命令行的命令基本上可以分为两类,一类是发送request请求以完成某些操作,另一类则是通过获取DCS中的信息来从而展示或进行修改。
3.1 管理信息命令
这类命令有list、remove、topology、dsn、edit-config、history、query、show-config、version。
-
list
list
命令的作用是输出指定条件的集群信息。其中output_members
函数是重要的输出函数,获取的集群信息都是调用这个函数输出在命令行中。其中便是调用工具模块(utils.py
)中的cluster_as_json
函数,将一个集群信息转换为处理过后的json字符串,然后从这个json字符串中获取所有的信息输出展示即可。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml list demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml list demo --group 1
list
命令的流程图如下所示: -
remove
remove
命令的作用是删除 DCS 中保存的指定集群的信息。在其中会验证是否使用的是分布式数据库,如果是分布式数据库必须要传入group
参数才能删除,删除调用的是 DCS 中实现的删除集群函数delete_cluster
。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
remove
命令的流程图如下所示: -
topology
topology
命令的作用是打印给定集群的拓扑结构信息,根据传入的参数不同而展示形态不同。这个命令的实现主要是依赖于list
命令,是对list命令输出集群信息的格式的改变。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml topology demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml topology demo --group 1
topology
命令的流程图如下所示: -
dsn
dsn
命令的作用是获取Patroni集群的一个成员的连接字符串。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml dsn demo -r primary patronictl -c /usr/patroni/conf/patroni_postgresql.yml dsn demo -r primary --group 1
dsn
命令的流程图如下所示: -
edit-config
edit-config
命令的作用是更改集群的动态配置并更新DCS。这个命令修改的配置是在保存在DCS中,其路径为/namespace/scope/group/config
。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml edit-config demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml edit-config --group 1 --pg max_connections="150" demo
edit-config
命令的流程图如下所示: -
history
history
命令的作用是展示集群的历史记录。这个命令查看的信息是保存在 DCS 中的,其路径为/namespace/scope/group/history
。获取到这些信息之后,还会创建一个表头,并且根据实际信息来扩展表头的信息以便输出内容的匹配。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml history demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml history demo --group 1
history
命令的流程图如下所示: -
query
query
命令的作用是针对Patroni集群的成员执行SQL命令或脚本。在其中会对 SQL 进行处理,并且根据传入的参数构造连接参数,所以要注意连接参数的传递。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml query demo -U fbase -c "SELECT now()" patronictl -c /usr/patroni/conf/patroni_postgresql.yml query demo --group 0 -r primary -U fbase -c "SELECT now()"
query
命令的流程图如下所示: -
show-config
show-config
命令的作用是展示zk中存储的配置信息。其路径为/namespace/scope/group/config
。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml show-config demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml show-config demo --group 1
show-config
命令的流程图如下所示:
3.2 发送请求命令
这类命令有restart、switchover、failover、pause、resume、reinit、reload、flush。
-
restart
restart
命令的作用是对指定的成员或者集群进行重启数据库操作(可以预定计划)。在其中如果之前有预定的重启计划,会删除之前的重启计划,再发送当前的重启计划。if 'schedule' in content:if force and member.data.get('scheduled_restart'):r = request_patroni(member, 'delete', 'restart')check_response(r, member.name, 'flush scheduled restart', True)r = request_patroni(member, 'post', 'restart', content)
使用命令如下所示:
patronictl -c /usr/patroni/conf/patroni_postgresql.yml restart demo pgsql1 patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
restart
命令的流程图如下所示: -
switchover
switchover
命令的作用是对指定的集群进行主备切换操作(可以预定计划)。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml switchover demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml switchover demo --group 1
switchover
命令的流程在1.1.2中已经详细介绍。 -
failover
failover
命令的作用对指定的集群进行故障转移操作(可以预定计划)。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
failover
命令的流程在1.1.2中已经详细介绍。 -
pause
pause
命令的作用是暂时将指定集群置于维护模式并禁用自动故障转移。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
pause
命令的流程图如下所示: -
resume
resume
命令的作用是用于恢复指定集群的自动故障转移功能。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
resume
命令的流程图与pause
一致,只是调用toggle_pause
传递的参数不同,pause
传递的pause
为True
,resume
传递的为False
。 -
reinit
reinit
命令的作用是将指定的备库进行重新初始化。这个命令只针对备库,只有备库才能重新初始化,在这个命令行会对符合条件的备库都发送一个重新初始化的请求,等待所有接单重新初始化之后,会发送一个get请求查看是否初始化完成。r = request_patroni(member, 'post', 'reinitialize', body)data = json.loads(request_patroni(member, 'get', 'patroni').data.decode('utf-8'))
使用命令如下所示:
patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
reinit
命令的流程图如下所示: -
reload
reload
命令的作用是当修改了某个节点patroni的配置文件时,使用该命令来重新加载配置文件。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml remove demo --group 1
reload
命令的流程图如下所示: -
flush
flush
命令的作用是丢弃计划的事件(如果有)。这个命令目前只支持放弃restart
或者switchover
计划,会先遍历找到的所有符合条件的成员(switchover
计划会首选主节点),然后判断该成员是否有这些计划,有的话就会发送一个delete
形式的request
来放弃这些计划,在放弃switchover计划之后还会清理DCS中保存的信息。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml flush demo restart -r replica patronictl -c /usr/patroni/conf/patroni_postgresql.yml flush demo switchover
flush
命令的流程图如下所示: -
version
version
命令的作用是展示指定成员的patronictl
和postgresql
的版本。在其中会先输出当前patronictl
的版本,然后查找到指定成员,发送request
请求来获取成员的 pg 的版本信息。使用命令如下所示:patronictl -c /usr/patroni/conf/patroni_postgresql.yml version demo patronictl -c /usr/patroni/conf/patroni_postgresql.yml version demo --group 1
version
命令的流程图如下所示: