Files
2026-05-24 11:42:38 +08:00

14 KiB
Raw Permalink Blame History

tags, create time
tags create time
MySQL
MHA
Orchestrator
高可用
故障切换
MySQL HA
2026-05-16 14:30

MHA / Orchestrator 故障切换

概述

当主库宕机时,如何快速且正确地选出一个新的主库并完成流量切换?这就是高可用(HA)系统的核心问题——既要尽量减少停机时间,又要保证数据不丢失、不发生冲突。

本节将对比两种主流的 MySQL 自动故障切换方案:

  • MHA:由日本 DeNA 开发的经典 Perl 方案,以数据一致性著称
  • Orchestrator:由 Spotify 开发的现代 Go 工具,侧重拓扑管理和运维便利

[!QUESTION] 核心思考 在讨论具体工具之前,先问一个问题:如果只有一个从库,它应该立刻提升为主库吗?

答案并不简单。如果主库只是短暂的网络抖动,切换就是"过度响应";但如果主库真的挂了还不切,服务就不可用了。如何在「反应太快」和「反应太慢」之间找到平衡,是 HA 设计的永恒命题。

故障场景分类

flowchart TD
    A["主库异常"] --> Type{"异常类型"}
    
    Type -->|"进程挂死"<| H1["检测快<br/>切换快<br/>可自动处理"]
    Type -->|"磁盘损坏"<| H2["数据可能损坏<br/>需人工介入"]
    Type -->|"脑裂 Split-Brain"<| H3["最危险!<br/>双主写入导致数据冲突"]
    Type -->|"网络分区"<| H4["隔离误判<br/>假阳性切换"]
    
    style H3 fill:#EE5A24,color:#fff

[!NOTE] 关键认知 HA 工具能自动处理的大多是进程级故障。一旦发生磁盘损坏或网络脑裂,就需要人工判断——这也是为什么理解底层原理比单纯部署工具更重要。

MHA(Master High Availability)

由日本 DeNA 开发(2009年)的经典 MySQL HA 方案,以 Perl 编写。其核心设计思想是**「绝不丢失已提交的数据」**——在故障切换前,会尽力补齐最从库缺失的 binlog 事件。

sequenceDiagram
    participant M as MHA Manager
    participant DM as Dead Master
    participant MS as Most Up-to-Date Slave
    participant SS as Other Slaves
    
    M->>DM: ping... no response
    Note over M: 连续 3 次 ping 失败
    M->>MS: SHOW MASTER STATUS
    MS-->>M: relay log 最新的位置
    M->>SS: SHOW RELAYLOG STATUS
    SS-->>M: relay log 较旧的位置
    
    Note over M: Step 1: 补齐最从库的 missing events
    M->>MS: 应用其他 slave 的 relay log 中的缺失事件
    Note over M: Step 2: 提升新主
    
    M->>MS: CHANGE MASTER TO... (无 master)
    Note over MS: 脱离子复制链,成为独立主库
    
    Note over M: Step 3: 重新配置其他从库指向新主
    
    M->>SS: CHANGE MASTER TO master=new-master-ip
    Note over SS: 全部切换到新主

MHA 的核心优势

特性 说明
数据一致性保证 选择 relay log 最新的 slave 作为新主
自动补齐 从其他 slave 获取 dead master 尚未传的 binlog
VIP 漂移 通过 Keepalived + VIP 实现无缝切换
Phantom Manager Manager 节点本身也可以做数据管理

[!TIP] 为什么选「最从」而非「任意从」?

假设 Master → SlaveA → SlaveB 的级联复制结构:

  • Master 挂了,SlaveA 可能比 SlaveB 更「新」
  • 如果直接提升 SlaveB,需要等待它追完所有relay log,延时长且风险大
  • MHA 的做法是:找到 relay log 最全的那个从库,确保数据丢失最小

MHA 组成

MHA 采用 Manager + Node 的分布式架构:

Manager Node          ← 运行 mha_manager,独立部署(不放在 MySQL 节点上)
Master Node           ← 当前主库(可挂,不影响 Node 组件)
Slaves Nodes          ← 多个从库(自动检测哪个最新)
App Servers           ← 应用端通过 VIP 访问

[!NOTE] 部署建议

  • Manager 应部署在独立的第三方机器上,不要和任何 MySQL 实例同机
  • 所有节点之间必须配置 SSH 互信,Manager 需要通过 SSH 执行远程操作

安装与配置

# 安装 RPM 包(CentOS/RHEL 7+)
yum install mha4mysql-manager mha4mysql-node -y
# 配置文件 /etc/masterha/app1.cnf
[server default]
manager_workdir=/data/mha/app1
manager_log=/data/mha/app1/manager.log
user=mha_user            # Manager 连接 MySQL 的管理账号
password=mha_pass
ssh_user=root            # SSH 连接账号
repl_user=repl_user      # 主从复制账号
repl_password=repl_pass

[server1]
hostname=10.0.1.10
candidate_master=1       # 允许被提升为主库

[server2]
hostname=10.0.1.11
candidate_master=1

[server3]
hostname=10.0.1.12
no_master=1              # 永远不做主库(例如数据量特别大的从库)

[!TIP] candidate_master 的作用

默认情况下,MHA 优先选择 relay log 最新的从库,而不看它离 Master 的距离。但如果一个从库是「级联」链路末端(Master → S1 → S2),即使 S2 的 relay log 最全,也不应该提升它——因为它一旦变主,其他从库要重新追很长的 binlog。

candidate_master=1 可以告诉 MHA:这个节点虽然可能不是 relay log 最完整的,但仍然有资格被提升。配合 report_script 还可以加入更多判断逻辑。

GTID 支持

MHA 0.56+ 版本开始支持 GTID 模式下的故障切换:

# app1.cnf 中开启 GTID 模式
[server default]
master_binlog_dir=/var/lib/mysql
remote_workdir=/tmp/mha
use_gtid_current_pos=1   # 使用 GTID 定位复制位置

[!NOTE] GTID vs Position 的选择

方式 优点 缺点
binlog position 兼容所有 MySQL 版本 需要精确计算每个 slave 丢失的事件
GTID current_pos 简化了定位逻辑,切换更可靠 要求 MySQL 5.6+,且需提前开启 GTID

如果生产环境已启用 GTID,强烈建议使用 GTID 模式;否则保持默认的 binlog position 即可。

MHA 的局限

[!WARNING] MHA 的问题

  1. Manager 单点:虽然可以配 Phantom,但需要一个额外的管理入口
  2. Perl 技术栈:生态维护活跃度下降
  3. 不支持 GTID 之前:GTID 出现前依赖 binlog position
  4. 脑裂风险:如果 Master 只是网络闪断而非真宕机,可能出现双主

替代方案:Orchestrator + Go-HAProxy 或 Patroni(PostgreSQL 方向)

Orchestrator

由 Spotify 开源的纯 Go 编写的 MySQL 拓扑发现和运维工具。核心定位是**「MySQL 拓扑的管理员」**——它首先帮你搞清楚「现在整个集群是什么样的」,然后在此基础上提供故障切换、手动切换、修复落后从库等操作。

flowchart TB
    subgraph "Orchestrator 架构"
        OAPI["Orchestrator HTTP API<br/>port 3000"]
        ODB["SQLite/MySQL<br/>存储拓扑信息"]
        OSCAN["Discovery Scan<br/>定期 SHOW SLAVE HOSTS"]
        
        frontend["Web UI<br/>拓扑可视化"] --> OAPI
        dashboard["Dashboard"] --> OAPI
        
        OSCAN --> ODB
        OAPI --> ODB
        
        HAProxy["Go-HAProxy / <br/>MaxScale / ProxySQL"] -->|"重定向流量"| NEWMASTER["新主库"]
    end
    
    OAPI --> HAProxy

[!NOTE] 设计理念差异

维度 MHA Orchestrator
首要目标 自动故障切换(Failover) 拓扑管理 + 运维辅助
侵入性 需要在每个节点安装 node 组件 零侵入,只读查询
执行方式 Manager 直接 SSH 操作各节点 通过 API 调用,外部脚本接管
技术栈 Perl Go(单二进制文件部署)

Orchestrator 如何工作

Orchestrator 的核心是一个后台扫描循环:

每 5~30 秒 ──┬──► SHOW SLAVE HOSTS / SHOW REPLICA STATUS
             ├──► 读取各实例的 Binlog Position
             ├──► 构建完整拓扑树(含级联复制关系)
             └──► 写入内部数据库 → Web UI 刷新显示

这意味着 Orchestrator 不主动修改任何 MySQL 配置,它只是一个观察者 + 操作台。所有的故障切换需要外部工具(如 Go-HAProxy)配合完成。

自动故障切换(Auto-Failover)

虽然 Orchestrator 以拓扑管理见长,但它同样支持全自动故障切换:

# orchestrator.conf.json 关键配置
{
    "DiscoverByShowSlaveHosts": true,
    "DetectMasterFailoverWithSuperReadOnly": false,
    "DetectorScript": "/usr/local/bin/orchestrator-detector.sh",
    
    // 自动 Failover 的条件控制
    "AutoFaildown": true,       // Master 恢复后是否自动降级并重新纳入复制链
    "AutoFailop": true,         // Master 宕机时是否自动切换
    "ExpireStatsSec": 600       // 统计信息过期时间
    
    // 选择新主的策略
    "PreferredCandidateDistances": [1, 2],      // 优先选距离近的从库
    "CheckSlaveLagThreadsCached": true            // 自动检测 Slave 延迟
}

[!QUESTION] Orchestrator 为什么不直接改 VIP?

MHA 内置了 VIP 漂移功能,但 Orchestrator 选择了更松耦合的设计。原因是:

  1. 不同环境使用不同的代理层(HAProxy / ProxySQL / MaxScale / 云厂商 LB)
  2. 让外部系统决定「怎么切流量」更加灵活
  3. Orchestrator 专注于做一件事:准确知道拓扑结构并给出操作建议

生产环境中常见的做法是写一个 wrapper 脚本,当 Orchestrator 检测到 Master 挂掉时自动调用它来切换 ProxySQL 或 HAProxy 的后端配置。

常用 API 示例

# 1. 查看某个应用下的所有实例拓扑
curl http://localhost:3000/api/topology/app/mydb

# 2. 找到最适合晋升为主库的候选者
curl http://localhost:3000/api/candidate-instances/app/mydb

# 3. 强制提升某实例为主库(手动干预)
curl -X POST http://localhost:3000/api/become-master/host/db/instance

# 4. 自动修复脱离的从库(重新对接到新主)
curl -X POST http://localhost:3000/api/recover/full/host/db/instance

# 5. 获取故障切换分析(不实际执行,只看推荐方案)
curl http://localhost:3000/api/fail-coordination/force/master-simulate/host/db/instance

[!TIP] Simulate 模式

fail-coordination 接口支持模拟切换流程而不真正执行。在生产环境中强烈建议先用这个命令演练一次,确认推荐的切换方案符合预期后再正式执行。

脑裂的处理

flowchart TD
    A["网络分区导致<br/>Master 与 Slave 失联"] --> B{"HA 工具判断<br/>Master 存活?"}
    
    B -->|否| C["提升 Slave 为新主"]
    C --> D{"原 Master 恢复后<br/>是否还活着?"}
    D -->|是| E["⚠️ 双主!两个库都在接受写请求"]
    E --> F["用 binlog 比对<br/>找出冲突数据"]
    F --> G["回滚冲突数据<br/>或将原 Master 降级为从库"]
    
    D -->|否| H["原 Master 重启后<br/>作为从库接入"]
    
    B -->|是| I["不做切换,继续等待"]
    
    style E fill:#EE5A24,color:#fff
    style H fill:#00D866,color:#fff

脑裂后的数据恢复步骤

当确认发生脑裂时,标准处理流程如下:

Step 1  ──► 停止新主的写入(防止更多冲突)
Step 2  ──► 导出两个库的 GTID 集合或 binlog position 范围
Step 3  ──► 使用 mysqlbinlog + diff 对比差异事件
Step 4  ──► 保留「正确」分支的数据
Step 5  ──► 将另一方的冲突数据标记为 rollback 或直接丢弃
Step 6  ──► 重建复制链路

[!WARNING] 预防脑裂的策略

  1. STONITH(Shoot The Other Node In The Head):通过 IPMI / 远程管理卡强制关闭疑似脑裂节点——这是最彻底的方案
  2. Quorum(多数派机制):3 节点集群中,至少 2 节点存活才允许选举,避免少数派擅自决策
  3. 带外心跳:除了 TCP ping,再用 SSH 或其他独立通道探测,降低单通道误判概率
  4. 延迟备份:保留一台物理隔离的冷备,脑裂时可从中恢复,相当于最后的保险丝
  5. 超时阈值调优:不要为了减少误切把超时设得太长(如超过 30s),故障期间每一秒都很宝贵

选型建议

flowchart LR
    A["需要 MySQL HA<br/>自动故障切换"] --> B{"技术栈偏好?"}
    
    B -->|"传统运维体系<br/>已有 Perl 团队"| MHA["MHA<br/>经典稳定"]
    B -->|"Go / 云原生<br/>需要 Web UI"| ORC["Orchestrator<br/>灵活现代"]
    B -->|"想要官方支持"<| INN["InnoDB Cluster<br/>MySQL Shell"]
    B -->|"不想自己维护 HA"| CLOUD["云厂商 RDS<br/>托管方案"]
    
    style MHA fill:#4A90D9,color:#fff
    style ORC fill:#00D866,color:#fff
    style INN fill:#F5A623,color:#fff
    style CLOUD fill:#EE5A24,color:#fff

关键对比维度

维度 MHA Orchestrator InnoDB Cluster
数据丢失 极小(自动补齐 binlog) 较小(依赖 GTID) 零(基于 Group Replication)
部署复杂度 中(需 SSH 互信 + RPM) 低(单二进制文件) 高(需 PXC/Galera)
故障恢复时间 通常 < 10s 通常 < 15s 秒级但检测较慢
在线扩展 不支持 仅拓扑管理,不处理 DDL 支持分布式 DDL
社区活跃度 较低(DeNA 维护) 高(VK / 社区活跃) 官方维护
适用 MySQL 版本 5.6~5.7 5.6+(推荐 8.0) 8.0+

[!TIP] 实际生产中的常见选择

  • 中小团队(< 10 台 MySQL 实例):Orchestrator 是性价比最高的选择——上手简单、维护成本低、Web UI 对 DBA 友好
  • 大流量业务对数据一致性要求极高:如果预算允许,上云厂商 RDS 的付费高可用版是最省心的
  • 自建全量多主写入:考虑 InnoDB Group Replication 或 ProxySQL + Orchestrator 组合

关联笔记