
数据库高可用架构设计从MHA到Orchestrator的故障切换方案演进一、主库挂了切换脚本没生效业务瘫痪了47分钟凌晨的主库宕机修复了但故障切换脚本因为一个未知的状态判定错误在Master_Log_File判断阶段卡住了 47 分钟。当运维手动介入完成切换时已经错过了服务等级协议SLA的要求。MySQL 高可用方案的发展史其实就是故障切换从人工到自动化的演进史。从最初全人工的 M-S 切换到 MHA 的半自动化脚本再到 Orchestrator 的全自动拓扑管理与故障检测每一步都在减少人工判断的不确定性。本文将对比三种主流方案的架构差异与适用场景。二、三种高可用方案的架构对比flowchart TB subgraph MHA[MHA (Master High Availability)] M1[MHA Manager] -- M2[监控主库] M2 -- M3{主库宕机?} M3 --|是| M4[选新主br/基于Binlog位点] M4 -- M5[应用差异化Relay Log] M5 -- M6[切换VIP] end subgraph Orchestrator[Orchestrator] O1[Orchestrator 集群] -- O2[持续拓扑发现] O2 -- O3{主库不可达?} O3 --|是| O4[基于GTID选主br/多节点确认] O4 -- O5[执行优雅切换br/或强制故障转移] O5 -- O6[更新拓扑] end subgraph InnoDB_Cluster[MySQL InnoDB Cluster] I1[Group Replication] -- I2[自动故障检测] I2 -- I3{多数派决策} I3 -- I4[自动选主] I4 -- I5[MySQL Router 路由切换] end方案故障检测时间切换时间数据丢失风险适用规模MHA10-30 秒10-30 秒低小集群 10 节点Orchestrator5-10 秒15-60 秒极低中大集群100 节点InnoDB Cluster秒级 5 秒极低需多数派3-9 节点三、Orchestrator 部署与配置3.1 安装与基础配置{ Debug: false, ListenAddress: :3000, MySQLTopologyUser: orchestrator, MySQLTopologyPassword: password, MySQLOrchestratorHost: orchestrator-db, MySQLOrchestratorPort: 3306, MySQLOrchestratorDatabase: orchestrator, MySQLOrchestratorUser: orchestrator, MySQLOrchestratorPassword: password, DiscoverByShowSlaveHosts: false, InstancePollSeconds: 5, DiscoveryPollSeconds: 5, UnseenInstanceForgetHours: 240, SnapshotTopologiesIntervalHours: 0, RecoveryPollSeconds: 10, RecoveryPeriodBlockSeconds: 3600, RecoverMasterClusterFilters: [*], RecoverIntermediateMasterClusterFilters: [*], OnFailureDetectionProcesses: [ echo Detected {failureType} on {failureCluster}. Affected replicas: {countSlaves} /tmp/recovery.log ], PreFailoverProcesses: [ echo Will recover from {failureType} on {failureCluster} /tmp/recovery.log ], PostFailoverProcesses: [ echo Recovery completed on {failureCluster}. New master: {successorHost} /tmp/recovery.log, /usr/local/bin/update_vip.sh {successorHost} ], PostIntermediateMasterFailoverProcesses: [ echo Intermediate master failover on {failureCluster} /tmp/recovery.log ], PostUnsuccessfulFailoverProcesses: [ echo FAILOVER FAILED on {failureCluster} /tmp/recovery.log ], ApplyMySQLPromotionAfterMasterFailover: true, MasterFailoverLostInstancesDowntimed: true, DetachLostSlavesAfterMasterFailover: true, FailMasterPromotionOnLagMinutes: 1, FailMasterPromotionIfSQLThreadNotUpToDate: true, DelayMasterPromotionIfSQLThreadNotUpToDate: true, HostnameResolveMethod: default, MySQLHostnameResolveMethod: hostname, SkipBinlogServerUnresolveCheck: true, StatusEndpoint: /api/status }3.2 Orchestrator API 驱动的自动化运维#!/usr/bin/env python3 Orchestrator API 客户端故障切换编排 import requests import time from typing import Dict, List, Optional class OrchestratorClient: Orchestrator API 客户端 def __init__(self, base_url: str): self.base_url base_url.rstrip(/) def get_clusters(self) - List[str]: 获取所有集群列表 response requests.get(f{self.base_url}/api/clusters) response.raise_for_status() return response.json() def get_cluster_info(self, cluster_name: str) - Dict: 获取集群详细信息 response requests.get( f{self.base_url}/api/cluster/{cluster_name} ) response.raise_for_status() return response.json() def get_failure_detection(self) - List[Dict]: 查看当前故障检测状态 response requests.get( f{self.base_url}/api/check-global-recoveries ) response.raise_for_status() return response.json() def manual_failover(self, cluster_name: str) - Dict: 手动触发故障切换 response requests.post( f{self.base_url}/api/recover/{cluster_name} ) response.raise_for_status() return response.json() def graceful_takeover(self, cluster_name: str, new_master: str) - Dict: 优雅切换指定新主库 response requests.post( f{self.base_url}/api/graceful-master-takeover/{cluster_name}, params{designatedHost: new_master} ) if response.status_code 200: return response.json() else: raise Exception(f优雅切换失败: {response.text}) def health_check(self) - Dict: Orchestrator 自身健康检查 response requests.get(f{self.base_url}/api/health) return response.json() def verify_replication_health(self, cluster_name: str) - Dict: 验证集群复制健康状态 cluster_info self.get_cluster_info(cluster_name) issues [] for instance in cluster_info.get(Instances, []): # 检查每个从库是否正常复制 if instance.get(Slave_SQL_Running) ! Yes: issues.append({ host: instance[Key][Hostname], issue: SQL thread not running }) if instance.get(SecondsBehindMaster, {}).get(Int64, 0) 60: issues.append({ host: instance[Key][Hostname], issue: f延迟 {instance[SecondsBehindMaster][Int64]}s }) return { cluster: cluster_name, healthy: len(issues) 0, issues: issues }四、故障切换的四个关键决策决策一自动切换还是人工确认Orchestrator 自动切换适合多套库100 节点人工逐个确认不可行。但需要完善的防抖机制避免网络抖动误触发。MHA 半自动 人工确认适合核心库数据安全优先于切换速度。决策二选择哪个从库当新主优先级排序GTID 最超前 数据最新Binlog 位点 硬件配置最优 延迟最低。决策三旧主恢复后如何处理不要自动恢复旧主。应该以从库身份重新加入集群等数据同步完成后再考虑是否切换回去。决策四脑裂防护Orchestrator 通过 Raft 协议在多个 Orchestrator 节点间达成共识避免两个 Orchestrator 实例同时认为对方是主库的脑裂场景。五、总结数据库高可用架构的选择取决于业务对 RTO恢复时间目标和 RPO数据恢复点目标的要求MHA适合小团队、追求简单可控对 RTO 要求在分钟级的场景Orchestrator适合中大团队、需要管理多套数据库集群对自动化要求高的场景InnoDB Cluster适合全新架构、能接受 3 节点起步成本追求秒级切换的场景在实际部署中从 MHA 切换到 Orchestrator 后平均故障切换时间从 15 分钟含人工决策降到 45 秒全自动。最重要的是——Orchestrator 的拓扑可视化让团队第一次看清楚整个数据库集群的复制关系是什么样的这个可见性的价值甚至超出了切换速度本身。