一主三从 MySQL + 一主三备 Nginx 高可用切换全解
本文串联两个常见的高可用场景,并回答两个关键疑问:
- 一主三从 MySQL,靠MHA / Orchestrator / 云 RDS做自动切换——这三者是什么、自动切换怎么做到?
- 一主三备 Nginx +keepalived——keepalived 是什么、起什么作用、怎么配置和工作?主挂 → VIP 漂给其中一台备、其余备继续待命,这个 VIP 漂移到底是怎么实现的?
最后一节专门回答一个跨机房疑问:2 主 2 备时一个机房故障、备机接管了,流量是不是不用切到另一个机房?
基础的 keepalived/VRRP 原理见同目录 [[keepalived-VIP高可用原理与三个疑问解答]],本文不再重复,只讲进阶与多节点场景。
一、一主三从 MySQL 的自动切换:MHA / Orchestrator / 云 RDS
1. 先说清楚"一主三从"拓扑
┌── slave1 master ───────┼── slave2 ├── slave3 └── ... (一主多从)- 一主:唯一的可写节点 master,所有写都打到它。
- 三从:通过主从复制(binlog + relay log)从 master 同步数据,只读。
- 风险:master 挂了,整个集群就不能写了。需要一个"自动选新主、重建复制拓扑、把流量切过去"的机制——这就是 MHA / Orchestrator / 云 RDS 干的事。
关键点:自动切换 = 三件事:①故障检测 → ②选主 + 补数据 + 重建拓扑 → ③流量切到新主。三个产品用的是不同实现,但都逃不出这三步。
2. MHA(Master High Availability)
是什么:MySQL 早期最经典的高可用切换方案,由日本 DeNA 公司的 MySQL 专家山下宏(Yoshinori Matsunobu)用 Perl 编写,2010~2014 年活跃,之后基本停更。是"老一代"HA 的代表。
组成:
- MHA Manager:独立部署的管理节点,负责监控、决策、执行 failover。一台 Manager 可以管多个 MySQL 集群。
- MHA Node:装在每一台 MySQL(含 master 和所有 slave)上的代理,failover 时被 Manager 调用,负责读写 binlog、对比 relay log、应用差异日志。
工作原理(failover 全过程):
- 监控:Manager 每隔几秒 ping 一次 master(
mysqladmin ping+ 复制健康检查)。 - 故障判定:连续 N 次连不上 → 判定 master 宕机。
- 收集差异 binlog:Manager 连上所有 slave,从每台 slave 读取它们已收到的最新 binlog 位点,对比出"谁的数据最新、谁还差哪些 binlog 没应用"。
- 选主:默认选数据最新的 slave 当新 master(也可在配置里指定候选优先级)。
- 补数据:把"新 master 之外的 slave 比新 master 多出来的 binlog"先补到新 master 上,再把"新 master 比其他 slave 多出来的 binlog"应用到其他 slave——核心目的是尽量不丢数据。
- 提升 + 重建拓扑:新 master 停掉只读、设为可写;其余 slave
CHANGE MASTER TO指向新 master,复制恢复。 - 流量切换:MHA 本身只管数据库层,流量切换要靠外部——常见做法是 MHA failover 完成后触发脚本去改 ProxySQL / VIP / DNS,把写流量指到新 master。
优点:尽量减少数据丢失(差异 binlog 补齐机制是 MHA 的灵魂);纯软件、不依赖共享存储。
缺点:
- 已停更多年,对 MySQL 8.0+ 支持弱、GTID 支持不完善,需自行打补丁。
- Perl 依赖、配置繁琐、Manager 自身是单点(要再给 Manager 做高可用)。
- 不支持"原地恢复原 master"等更复杂拓扑操作。
3. Orchestrator
是什么:GitHub 开源、用 Go 编写的 MySQL 复制拓扑管理与高可用工具。是 MHA 的"现代继任者",活跃维护,社区主流。
核心特点:
- 拓扑自动发现:连进 MySQL 读
SHOW SLAVE STATUS等,自动画出主从拓扑图,持久化到后端 DB(MySQL/SQLite)。 - Web UI + JSON API:可视化看到整个复制树,鼠标点一下就能做"提升某台为主""移动子树"等操作。
- 复杂拓扑支持:不仅主从,还支持级联复制(链式)、双主、多源,能做拓扑重构(move/relocate replica)。
- 自动 failover:内置故障检测和恢复规则,支持候选主优先级、忽略节点、跨机房亲和性等约束。
- Pseudo-GTID:即使没有开 GTID、binlog 位点对不齐,也能靠"伪 GTID"做安全的拓扑重构。
- 流量切换集成:原生支持 failover 后调用钩子去操作ProxySQL / HAProxy / VIP / Consul / DNS,把流量引到新主。
工作原理(自动 failover):
- 探测:Orchestrator 持续探测所有节点(连接 + 复制状态),结果写入后端 DB。
- 故障检测:连续探测失败 + 复制断开 → 进入"recovery"流程。为防脑裂,可用orchestrator 仲裁节点(quorum)或共享后端 DB 锁来保证同一时刻只有一个 failover 在跑。
- 选主:按规则选 candidate——数据最新、不在黑名单、符合"同机房优先"等约束。
- 提升 + 重建拓扑:把候选提升为新主,其余 slave 重新挂到新主下,复制恢复。
- 钩子切换流量:触发 post-failover hook,更新 ProxySQL 的后端 / 漂 VIP / 改 DNS,让业务感知到新主。
优点:活跃维护、Go 单文件部署、UI 强大、拓扑操作丰富、和 ProxySQL 等生态集成好。
缺点:本身要保证高可用(后端 DB + orchestrator 多实例 + raft),架构比 MHA 复杂;规则多、学习曲线陡。
4. 云 RDS(阿里云/腾讯云/AWS RDS)
是什么:完全托管的高可用 MySQL——你只管用,看不到底层 binlog/复制细节,高可用由云厂商兜底。
典型实现(不公开但业界通行做法):
- 主备 + 共享存储 / DRBD / 存储级复制:主和备在底层共享同一份数据(或存储层实时复制),故障切换时备直接挂载已有数据,几乎不丢数据。
- 半同步复制:主写 binlog 至少有一个备确认收到才返回,降低丢数据概率。
- 切换由云控制面托管:监控到主不可用 → 仲裁 → 备提升为主 →DNS/VIP 自动指向新主,业务连接短暂中断后自动重连即可。
优点:零运维、切换秒级、有 SLA 兜底、自带备份/恢复/监控。
缺点:黑盒、被厂商锁定、跨可用区/跨地域架构受产品形态限制、成本高、定制能力弱。
5. 三者对比与"自动切换怎么做到"的小结
| 维度 | MHA | Orchestrator | 云 RDS |
|---|---|---|---|
| 形态 | 自管软件(Perl) | 自管软件(Go) | 托管服务 |
| 维护状态 | 基本停更 | 活跃 | 持续迭代 |
| 故障检测 | Manager ping | 探测 + 后端持久化 | 云控制面监控 |
| 选主/补数据 | 差异 binlog 补齐 | 拓扑重构 + Pseudo-GTID | 共享存储/半同步,切的是"挂载点" |
| 流量切换 | 外部脚本触发 | 内置 hook 调 ProxySQL/VIP/DNS | DNS/VIP 自动 |
| 适合 | 老集群、MySQL 5.x | 自建、需要复杂拓扑 | 不想自己运维、要 SLA |
一句话:自动切换的通用套路= 健康检查 → 仲裁判活(防脑裂)→ 选数据最新的备 → 提升 + 重建复制拓扑 → 调钩子把 VIP/ProxySQL/DNS 指到新主。三个产品的差别只是"谁来执行这几步、用什么机制补数据、流量切换谁来做"。
二、一主三备 Nginx + keepalived
1. keepalived 是什么
keepalived是一个跑在 Linux 上的高可用软件,核心基于VRRP 协议(Virtual Router Redundancy Protocol,虚拟路由冗余协议)。它本来是给 LVS 负载均衡器做高可用的,但因为能管理 VIP + 做进程健康检查,现在广泛用于"Nginx / HAProxy / 任何单点服务"的高可用。
keepalived 在这个场景里干两件事:
- 管理 VIP:让 VIP 同一时刻只绑在一台机器的网卡上,主挂了自动漂到备。
- 进程健康检查:通过
vrrp_script定期检查 Nginx 是否活着,Nginx 挂了即使机器还活着,也触发 VIP 漂移(这比"机器宕机才漂"更精细)。
VRRP 基础原理(MASTER/BACKUP 选举、priority 含义、广播超时漂移)见 [[keepalived-VIP高可用原理与三个疑问解答]]。下面只讲一主三备的进阶。
2. 一主三备的拓扑
外部流量 ──> VIP(192.168.1.100) ──> 当前 MASTER ┌──────────────┬──────────────┬──────────────┐ Nginx-1 Nginx-2 Nginx-3 Nginx-4 MASTER BACKUP BACKUP BACKUP priority 150 priority 140 priority 130 priority 120 (绑 VIP) (待命) (待命) (待命)- 四台机器同一个 VRID(虚拟路由器 ID),组成一个 VRRP 组,共用同一个 VIP。
- 同一时刻只有一台是 MASTER,绑着 VIP、响应 ARP、收流量;其余三台都是 BACKUP,不绑 VIP、不收流量,只是持续听 MASTER 的广播。
- priority 拉出阶梯(150/140/130/120),不是分流比例,而是**"谁先接班"的排队顺序**。
3. keepalived 配置示例
主(Nginx-1,priority 150)/etc/keepalived/keepalived.conf:
# 健康检查脚本:每 2 秒 curl 一次 Nginx,失败则本机 priority 减 20 vrrp_script chk_nginx { script "killall -0 nginx || curl -s -o /dev/null http://127.0.0.1/" interval 2 fall 2 rise 2 weight -20 } vrrp_instance VI_1 { state MASTER # 初始状态(仅初始,实际靠选举) interface eth0 virtual_router_id 51 # 四台必须一致 priority 150 # 主最高 advert_int 1 # 1 秒发一次 VRRP 广播 authentication { # 同组四台要一致 auth_type PASS auth_pass MyPwd123 } virtual_ipaddress { 192.168.1.100 # 对外的 VIP } track_script { chk_nginx # 把 Nginx 健康和 VRRP 状态绑定 } notify_master "/etc/keepalived/notify.sh master" notify_backup "/etc/keepalived/notify.sh backup" }备(Nginx-2/3/4):完全一样,只改state BACKUP和priority(140/130/120)。其余字段(VRID、VIP、认证口令、track_script)四台必须一致。
健康检查脚本的意义:没有它,只有机器宕机才漂 VIP;有了它,Nginx 进程挂了但机器还活着也会漂——weight -20让本机 priority 从 150 掉到 130,立刻低于其它备份中的某台,触发抢占切换。这是"应用级高可用"的关键。
4. keepalived 的工作流程
- 启动:四台同时发 VRRP 报文,按 priority 比大小,Nginx-1(150)当选 MASTER,绑 VIP、对外 ARP。
- 运行:MASTER 每 1 秒组播一个 VRRP advert(含自己的 priority);三台 BACKUP 持续收,“主还活着,我不动”。
- 健康检查:MASTER 上的
chk_nginx每 2 秒检查 Nginx。 - 故障:
- 若 Nginx-1 整机宕机 → 三台 BACKUP 收不到 advert,超时(约 3 秒)后其中 priority 最高的 Nginx-2(140)升级为 MASTER,绑 VIP、发免费 ARP。
- 若只是 Nginx-1 上的 Nginx 进程挂 →
weight -20把它的 priority 拉到 130,低于 Nginx-2 的 140,Nginx-2 抢占成为 MASTER。
- 恢复:Nginx-1 修好、priority 恢复到 150。默认
preempt开启,会抢回 MASTER(可设nopreempt避免来回震荡)。
5. keepalived 的作用总结
| 能力 | 说明 |
|---|---|
| VIP 管理 | 保证 VIP 同一时刻只在一台机器上,主挂自动漂到备 |
| VRRP 选举 | priority 决定排队顺序,故障时自动选接班人 |
| 进程健康检查 | vrrp_script让"应用挂"也能触发切换,不限于"机器挂" |
| 通知钩子 | notify_master/backup在切换时执行脚本(发告警、reload 等) |
| 负载均衡 | keepalived 本身还能配 IPVS 规则做 LVS,但本场景主要用 VIP 高可用 |
注意:keepalived不分流、不做负载均衡。一主三备里同一时刻 VIP 只在 MASTER 上,所有流量都打到 MASTER 一台,其余三台是纯待命的冷备。想要"三台都干活"就不能用主备 + VIP,而要换成前端加 LVS/Nginx 四层 LB 分流(见 [[keepalived-VIP高可用原理与三个疑问解答]] 的主主模式一节)。
三、主挂 → VIP 漂给其中一台备,其余继续待命:漂移是怎么实现的
这是 VRRP 状态机 + 免费 ARP 共同完成的,拆成三步:
1. 触发:BACKUP 收不到 MASTER 的广播
MASTER 每秒发一个 VRRP advert(组播到224.0.0.18),报文里带着 VRID 和自己的 priority。三台 BACKUP 持续监听。当连续3 个 advert_int + skew_time(默认约 3 秒)收不到 advert,BACKUP 判定 MASTER 已死。
三台 BACKUP 都会同时进入这个判断,但谁先升级取决于 priority:priority 最高的(Nginx-2,140)先发"我要当 MASTER"的 advert,其余两台(130/120)收到后发现自己 priority 更低,就退回 BACKUP 继续待命。所以最终只有一台升级,另外两台仍在待命——这正是题目说的"漂给其中一台备、其他备继续待命"。
2. 绑定:新 MASTER 把 VIP 绑到自己网卡
Nginx-2 升级为 MASTER 后,执行ip addr add 192.168.1.100 dev eth0,把 VIP 配到自己网卡上。这一步是本地的内核操作,VIP 从原 MASTER 网卡上摘下(原主已死,自然没了)、绑到新 MASTER 网卡上。
3. 通告:发免费 ARP(gratuitous ARP)刷新网络层
这是让外部流量真正漂过来的关键一步。光在本地绑 VIP 没用——上游交换机/路由器的 MAC 地址表里,"192.168.1.100 → 旧 MASTER 的 MAC"还是旧记录,包还是会发到旧 MASTER(已死,丢包)。
所以新 MASTER 立刻广播一个免费 ARP:内容是"192.168.1.100 对应的 MAC 是我(新 MASTER)"。交换机收到后刷新 MAC 地址表,之后发往 192.168.1.100 的二层帧就走新 MASTER 的端口了。流量这才真正漂过来。
①MASTER 挂 → ②BACKUP-2 超时升级 → ③ip addr add VIP → ④发免费 ARP → ⑤交换机刷新 MAC 表 → ⑥流量到新 MASTER一句话:VIP 漂移的本质 = VRRP 状态切换(决定谁来当新主)+ 免费 ARP(让网络层把 VIP 的流量引到新主),不是"路由改道",而是"二层 MAC 重新映射"。
4. 为什么其余两台继续待命
因为同一时刻只能有一个 MASTER。Nginx-2 升级后会继续每秒发 advert,Nginx-3/4 收到 advert、看到自己 priority 更低,就保持 BACKUP 状态,不绑 VIP、不收流量。整个组始终只有一台绑 VIP,这是 VRRP 协议保证的,和"有几台备"无关。
四、跨机房疑问:2 主 2 备,一个机房故障、备机接管了,流量还要不要切到另一机房?
你的判断:“备机接管了,那流量应该不用切到另一个机房吧?” ——方向是对的,但前提需要分清楚到底是"机房故障"还是"机房内主库故障"。
关键要把两种故障区分开:
1. 情况 A:机房内"主库"故障,备机还活着(不是整个机房挂)
典型拓扑(同机房主备):
机房A:master-A + slave-A 机房B:master-B + slave-B (A、B 之间可能互为主从做跨机房容灾)- master-A 挂了,但机房 A 本身没断电断网,slave-A 还活着。
- slave-A 提升为新主(同机房接管),VIP 仍在机房 A 内漂移,业务流量仍打在机房 A。
- 结论:流量不用切到机房 B。你的理解完全正确——这是"机房内故障转移",备机就近接管,流量原地不动。
2. 情况 B:整个"机房"故障(断电/断网/火灾,机房内全部节点都死)
这才是题目字面说的"机房故障"。
- 机房 A 整个挂了 → master-A 和 slave-A都死了,slave-A 没法接管。
- 必须跨机房把流量切到机房 B 的 master-B(或机房 B 的备提升),让机房 B 承担全部流量。
- 结论:真正的机房级故障,备机根本接管不了(它自己也挂了),必须切到另一个机房。题目里"备机接管了"这个前提,在机房级故障下不成立。
3. 回到你的问题
“一个机房故障,备机接管了,那么流量不需要切到另一个机房吧?”
- 如果"故障"指的是机房内主库挂了、备机(同机房)还活着能接管 →对,流量不切机房,原地漂 VIP 即可。
- 如果"故障"指的是整个机房挂了、机房内所有节点(含备机)都死 →必须切到另一个机房,因为备机也死了、没东西可接管。
所以正确的表述应该是:
| 故障级别 | 备机能否接管 | 流量是否切机房 |
|---|---|---|
| 机房内主库故障 | 能(同机房备接管) | 否,原地漂 VIP |
| 机房级故障(整机房挂) | 不能(备也挂了) | 是,跨机房切到存活机房 |
4. 跨机房切换还要注意的事
真正切机房时,光"数据库切过去"不够,还要同步处理:
- 流量入口切换:DNS / 全局 LB / VIP(跨机房 VIP 漂移要看二层是否打通,往往用 DNS 或上层 LB 更现实)把流量引到机房 B。
- 数据一致性:跨机房复制有延迟,切过去时可能丢未同步的写;要靠半同步复制 + 回退窗口或分布式事务/对账兜底。
- 回切:机房 A 恢复后,要先把机房 A 的数据从机房 B 同步回来、确认一致后再切回,避免回切导致数据回退。
一句话总结:"备机接管"发生在机房内还是跨机房,取决于备机本身有没有跟着挂。机房内故障→原地接管、流量不动;机房级故障→备机也死、必须跨机房切。你"不用切机房"的直觉,准确对应的是前者。
五、整张图:从数据库到接入层的高可用全貌
外部流量 │ ┌─────┴─────┐ │ VIP/LB │ ← keepalived 管 VIP,主挂漂到备 └─────┬─────┘ ┌──────────────┼──────────────┐ Nginx-1(主) Nginx-2(备) Nginx-3/4(备) ← 一主多备,同时刻只一台绑 VIP │ 应用层 (写打 master) │ ┌─────┴─────┐ │ MySQL HA │ ← MHA/Orchestrator/云RDS 管选主+拓扑重建 └─────┬─────┘ ┌──────────────┼──────────────┐ master slave1 slave2/slave3 ← 一主三从,主挂选最新备提升 │ (跨机房) master-B + slave-B ← 机房级故障时切到存活机房六、三句话回顾全文
- 一主三从 MySQL 自动切换:MHA(差异 binlog 补齐,停更)、Orchestrator(Go,活跃,拓扑重构+钩子切流量)、云 RDS(托管,共享存储/半同步)——都遵循"健康检查→仲裁选主→补数据提升→流量切过去"四步套路。
- 一主三备 Nginx + keepalived:keepalived 靠 VRRP 让同一时刻只有一台 MASTER 绑 VIP,priority 排队决定接班顺序,
vrrp_script把 Nginx 进程健康绑进来,主挂/Nginx 挂都触发漂移。它不分流,三台备是纯待命。 - VIP 漂移本质:VRRP 状态切换(选新主)+ 免费 ARP(刷新交换机 MAC 表,把 VIP 流量引到新主)。跨机房疑问:机房内主库故障→同机房备接管、流量不动;机房级故障→备也死、必须跨机房切。