综合 主库失联时先问从库:orchestrator 的 DeadMaster 判据与自动恢复三个闸门

2026-11-10 00:04:57

主库失联时先问从库:orchestrator 的 DeadMaster 判据与自动恢复三个闸门

项目:openark/orchestrator(已归档)、percona/orchestrator(活跃维护)、文档。本文只谈一个切面:故障检测为什么依赖拓扑共识,UnreachableMaster 与 DeadMaster 的分界在哪,以及检测到故障之后,哪些开关会让 orchestrator 选择不自动恢复。

只探测主库会产生误判

常规监控的做法是定时探测主库,失联就报警。网络抖动会被误判为宕机;为减少误报再加“连续 n 次、间隔 t”重试,真实故障的响应时间又会被拖长。orchestrator 换了一种判据:不只检查主库,也看主库的从库。

判定一台主库为 DeadMaster,需要同时满足两个条件:

  1. 连不上该主库;
  2. 能连上该主库的从库,且这些从库也确认看不到主库。

这里用拓扑中的多个观察者替代时间窗口。当所有从库一致认为连不上主库时,拓扑事实上已经断裂,切换才有依据。单台监控机与主库之间的网络问题,不会被直接判成主库死亡。

所以 UnreachableMaster 和 DeadMaster 的差别不在“主库能不能连上”,而在“从库怎么看”:

  • DeadMaster:主库 MySQL 访问失败,主库所有从库复制失败。可以进入恢复流程。
  • UnreachableMaster:主库访问失败,但仍有从库在正常复制。不做恢复。

UnreachableMaster 还带一个补充动作:orchestrator 会对从库发起 emergent re-read,判断它们是真的对主库满意(可能只是自身网络抖动,看不到主库),还是正在花时间才发现复制失败。前者不该切,后者可能很快演变成 DeadMaster。

相邻的 DeadMasterAndSomeReplicas 是主库访问失败、部分从库也不可达、其余从库复制失败。UnreachableMasterWithLaggingReplicas 则出现在主库过载的场景:客户端看到 Too many connections,早已连上的从库却认为主库正常;或者主库被元数据操作锁住,客户端连接阻塞而从库宣称一切正常。应用连不上主库,没有真实写入,配 pt-heartbeat 这类心跳能看到从库延迟增长。orchestrator 的处理是重启主库所有直接从库的复制,关掉这些从库上的旧客户端连接并尝试新建;新连接此时可能失败,导致全部从库复制失败,随后被分析为 DeadMaster。

中间层同理:DeadIntermediateMaster 指带从库的从库不可达,且其全部从库复制失败,可进入恢复流程。

这些判断建立在轮询之上。默认每 InstancePollSeconds(60 秒)才轮询一次实例,看到的任何状态本质都是估计值;不同实例的轮询时刻不同,集群页面不是某一时刻的快照,而是一分钟内多个时刻的组合。完整发现拓扑可能要几分钟,取决于拓扑深度——从库的从库越多越慢,因为实例独立轮询,拓扑认知要逐轮从主库传播到从库。刚切到新主库后,几分钟内拓扑看似为空也属正常:实例仍自称属于正在被销毁的旧拓扑,之后会自愈。

检测不等于恢复:三个闸门

检测始终开启,每次检测都会执行 OnFailureDetectionProcesses 钩子。但检测到故障,并不代表会动手切换。自动化恢复是 opt-in 的。

第一道:集群是否在自动恢复名单里。

RecoverMasterClusterFilters=["thiscluster","thatcluster"]
RecoverIntermediateMasterClusterFilters=["*"]

这组配置的含义是:对所有集群自动恢复中间主库故障;只对列出的两个集群自动恢复主库故障,其他集群的主库故障等人工处理。未列入名单的集群,检测继续,恢复不动。

第二道:实例级和全局的否决。

管理员可以对特定实例标记不恢复;RecoveryIgnoreHostnameFilters 按主机名过滤;也可以全局关闭恢复;或者故障类型被认为不值得恢复。PreFailoverProcesses 里的任一进程非零退出,同样会中止恢复,可以用它按内部状态否决一次切换。

第三道:时间窗防抖。

RecoveryPeriodBlockSeconds=3600

同一集群刚完成恢复,3600 秒(1 小时)内阻止再次自动恢复,防止来回切。注意它与 FailureDetectionPeriodBlockMinutes=60 不是一回事:后者用于阻止同一检测反复通知,前者才是恢复侧的 anti-flapping。orchestrator 每秒执行检测,这两个时间量作用在不同层。

三个闸门任意一个不通过,检测结果都会停在“已发现、未处理”。

semi-sync 卡死:默认什么都不做

LockedSemiSyncMaster 的触发条件有三条:

  1. 主库开启半同步,rpl_semi_sync_master_enabled=1;
  2. 已连接的半同步从库数少于期望的 rpl_semi_sync_master_wait_for_slave_count;
  3. rpl_semi_sync_master_timeout 足够大,使主库锁住写入而不退回异步。

该条件只在 ReasonableLockedSemiSyncMasterSeconds 之后触发;若该项未设置,则按 ReasonableReplicationLagSeconds 触发。

处置方式有两种:主库关半同步,或补齐、启用足够半同步从库。orchestrator 提供两个开关:

  • EnforceExactSemiSyncReplicas:按优先级顺序与主库 wait count 计算期望的半同步拓扑,开关从库半同步;
  • RecoverLockedSemiSyncMaster:按优先级顺序启用(从不关闭)半同步从库,直到数量匹配 wait count。

EnforceExactSemiSyncReplicas 设置后,RecoverLockedSemiSyncMaster 无效。优先级由 DetectSemiSyncEnforcedQuery(数字大者优先)、提升规则 DetectPromotionRuleQuery、主机名兜底决定。

两者都关闭(默认)时,这类分析不触发任何恢复流程。MasterWithTooManySemiSyncReplicas 同理:主库开启半同步、已连接半同步从库数高于期望 wait count,仅在 EnforceExactSemiSyncReplicas 开启时才分析。默认配置下,semi-sync 卡住只报不动。

另外,普通叶子从库故障、复制延迟即使很严重,也不在关心范围里——不触发检测钩子,也不尝试恢复。例外是引发 LockedSemiSyncMaster 的半同步从库。

提升前后的互斥与前置

真正走恢复流程时,提升动作有几组需要留意的开关。

ApplyMySQLPromotionAfterMasterFailover 默认 true,为 true 时会对被提升主库执行 reset slave all 与 set read_only=0,并覆盖 MasterFailoverDetachSlaveMasterHost。MasterFailoverDetachReplicaMasterHost 默认 false,为 true 时对提升后的主库执行 detach-replica-master-host,防止旧主库复活后新主库反向复制;但前者为 true 时,这一项没有意义。

跨数据中心和跨 region 的替换默认都不限制:PreventCrossDataCenterMasterFailover 默认 false,PreventCrossRegionMasterFailover 默认 false。设为 true 后只用同数据中心(或同 region)实例替换故障主库,找不到则失败退出,分别依赖 DetectDataCenterQuery/DataCenterPattern 与 DetectRegionQuery/RegionPattern。

延迟相关的一组互斥更直接:

  • FailMasterPromotionIfSQLThreadNotUpToDate:所有从库都在延迟时,即使最新的被提升从库也可能有未应用的 relay log,对其执行 reset slave all 会丢掉 relay log 数据,自行权衡;
  • DelayMasterPromotionIfSQLThreadNotUpToDate:为 true 时等 SQL 线程追平再提升。

这两项互斥。

FailMasterPromotionOnLagMinutes 默认 0,不因延迟失败;可让候选从库落后太多时放弃提升。例如从库断了 5 小时后主库故障,可能想保留 binlog/relay log 而不切换。用该参数必须设置 ReplicationLagQuery 并使用 pt-heartbeat 之类心跳,因为 MySQL 内建 SHOW SLAVE STATUS 的 Seconds_behind_master(8.0 之前)在复制断开时不报告延迟。

恢复过程中可能丢失部分从库。DetachLostReplicasAfterMasterFailover 为 true 时用 detach-replica 强行打断它们的复制,避免有人误以为它们还可用。MasterFailoverLostInstancesDowntimeMinutes 默认 0 表示禁用,否则给切换中丢失的服务器(含故障主库与丢失从库)打上该分钟数的 downtime。PostponeReplicaRecoveryOnLagMinutes 默认 0,崩溃恢复时延迟超过该分钟的从库被推迟到恢复流程后段才拉起,主库/中间主库选出并执行完之后;0 禁用。

让检测更快一步

MySQL 侧有两处能缩短“从库发现主库有问题”的时间:

set global slave_net_timeout = 4;

把从库与主库之间的心跳间隔压到约 2 秒,从库能更快发现故障;不设的话某些场景要一分钟才检测到。

CHANGE MASTER TO MASTER_CONNECT_RETRY=1, MASTER_RETRY_COUNT=86400;

复制失败后每 1 秒重连(默认 60 秒),短暂网络抖动可快速恢复复制,避免触发整体故障/恢复流程。

切换能否成功还取决于拓扑类型:Oracle/Percona + GTID 下,可提升服务器必须开 log_bin 与 log_slave_updates,从库必须 CHANGE MASTER TO MASTER_AUTO_POSITION=1;MariaDB GTID 同样要求可提升服务器开 log_bin 与 log_slave_updates;Pseudo GTID 还要求 5.7/8.0 并行复制设 slave_preserve_commit_order=1;BinlogServers 要求可提升服务器开 log_bin。

查看分析结果

orchestrator-client -c replication-analysis
orchestrator -c replication-analysis

Web API 为 /api/replication-analysis,页面在 /web/clusters-analysis/(Clusters → Failure analysis),只突出可操作项。检测钩子可以这样落日志:

echo 'Detected {failureType} on {failureCluster}. Affected replicas: {countReplicas}' >> /tmp/recovery.log

配置为 OnFailureDetectionProcesses。恢复侧钩子按定义顺序串行执行,命令以 & 结尾则异步执行且忽略失败;可用的环境变量包括 ORC_FAILURE_TYPE、ORC_INSTANCE_TYPE、ORC_IS_MASTER、ORC_FAILURE_CLUSTER、ORC_COUNT_REPLICAS、ORC_IS_DOWNTIMED、ORC_AUTO_MASTER_RECOVERY、ORC_AUTO_INTERMEDIATE_MASTER_RECOVERY、ORC_IS_SUCCESSFUL、ORC_LOST_REPLICAS、ORC_REPLICA_HOSTS、ORC_COMMAND 等,成功时另有 ORC_SUCCESSOR_HOST、ORC_SUCCESSOR_PORT、ORC_SUCCESSOR_BINLOG_COORDINATES、ORC_SUCCESSOR_ALIAS。命令行魔法变量对应 {failureType}、{instanceType}、{isMaster}、{failureCluster}、{countReplicas}、{isDowntimed}、{autoMasterRecovery}、{lostReplicas}、{replicaHosts}、{isSuccessful}、{command} 等。

推荐文章

程序员茄子在线接单