This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/MySQL/31-MHA与Orchestrator.md
T

346 lines
14 KiB
Markdown
Raw Normal View History

2026-05-17 00:06:11 +08:00
---
tags: [MySQL, MHA, Orchestrator, 高可用, 故障切换, MySQL HA]
create time: 2026-05-16 14:30
---
# MHA / Orchestrator 故障切换
## 概述
当主库宕机时,如何**快速且正确**地选出一个新的主库并完成流量切换?这就是高可用(HA)系统的核心问题——既要尽量减少停机时间,又要保证数据不丢失、不发生冲突。
本节将对比两种主流的 MySQL 自动故障切换方案:
- **MHA**:由日本 DeNA 开发的经典 Perl 方案,以数据一致性著称
- **Orchestrator**:由 Spotify 开发的现代 Go 工具,侧重拓扑管理和运维便利
> [!QUESTION] 核心思考
> 在讨论具体工具之前,先问一个问题:**如果只有一个从库,它应该立刻提升为主库吗?**
>
> 答案并不简单。如果主库只是短暂的网络抖动,切换就是"过度响应";但如果主库真的挂了还不切,服务就不可用了。如何在「反应太快」和「反应太慢」之间找到平衡,是 HA 设计的永恒命题。
## 故障场景分类
```mermaid
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 事件。
```mermaid
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 执行远程操作
#### 安装与配置
```bash
# 安装 RPM 包(CentOS/RHEL 7+)
yum install mha4mysql-manager mha4mysql-node -y
```
```bash
# 配置文件 /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 模式下的故障切换:
```ini
# 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 拓扑的管理员」**——它首先帮你搞清楚「现在整个集群是什么样的」,然后在此基础上提供故障切换、手动切换、修复落后从库等操作。
```mermaid
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 以拓扑管理见长,但它同样支持全自动故障切换:
```ini
# 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 示例
```bash
# 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` 接口支持模拟切换流程而不真正执行。在生产环境中强烈建议先用这个命令**演练一次**,确认推荐的切换方案符合预期后再正式执行。
## 脑裂的处理
```mermaid
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),故障期间每一秒都很宝贵
## 选型建议
```mermaid
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 组合
## 关联笔记
- [[hhs/MySQL/30-主从复制]] — 主从复制的配置和监控
- [[hhs/MySQL/33-Group Replication]] — 内置多主复制,不需要外部 HA 工具
- [[hhs/Redis/06-主从与哨兵]] — Redis Sentinel 与 MySQL HA 方案的对比