本文目录导读:

故障转移(Failover)的核心目标是:当系统中的一个组件(如服务器、数据库、网络)发生故障时,能够自动、无缝地将工作负载切换到另一个健康的备用组件上,从而保证服务的持续可用性。
实现故障转移的方式主要取决于你希望保护的资源类型(如IP、数据库、应用进程)和架构层级,以下是几种主流的实现方案及其原理:
网络层/硬件层故障转移(最基础)
这是最底层、最快的故障转移方式,通常依赖硬件或操作系统。
- VRRP / CARP 协议
- 原理:多台服务器(通常是2台)共享一个虚拟IP(VIP),其中一台是Master(主),负责响应VIP请求;另一台是Backup(备),主机会通过心跳报文宣告自己的存活,如果Backup在指定时间(如3秒)内收不到Master的心跳,就会接管VIP,成为新的Master。
- 应用:常用于防火墙、路由器的热备,或作为高可用集群的入口。
- 特点:透明、切换快(秒级),但对应用层故障(如HTTP服务挂起但进程还在)无感。
应用层/服务层故障转移(最常用)
这是针对特定服务进程的故障转移,需要一个集群管理软件(如下方提到的Keepalived、Heartbeat等套件)或容器编排平台。
-
主从模式
- 原理:一个主节点负责所有读写,一个或多个从节点只负责同步数据,当主节点宕机时,从节点之一被提升为新的主节点。
- 典型实现:Redis Sentinel、MySQL主从+ MHA、MongoDB Replica Set。
- 切换流程:
- Sentinel/仲裁节点检测到主节点超时。
- 多数Sentinel/从节点达成共识,确认主节点失效。
- 选举出一个从节点,执行
SLAVEOF NO ONE命令使其变为新主。 - 修改配置,将客户端连接指向新主。
-
主主模式
- 原理:两个节点都处于活动状态,各自处理请求,但通常需要配合流量入口做负载均衡,当一个节点故障时,流量全部切换给另一个。
- 难点:需要解决数据冲突和写入一致性问题(如MySQL双写),生产环境使用较少,常见于无状态服务。
数据层故障转移(最复杂)
数据层的故障转移除了要恢复服务,还必须保证数据不丢、一致性不破坏。
-
共享存储 + 挂载切换
- 原理:所有节点访问同一个共享存储(如NAS、SAN、Ceph RBD),应用进程运行在活动的节点上,数据则从共享存储读取。
- 切换:当节点A故障时,节点B接管挂载点(如通过
fsck强制挂载),启动自己的服务进程,并对外提供服务,数据库集群(如Oracle RAC)是典型代表。
-
应用层数据复制 + 重定向
- 原理:数据在多个节点之间实时同步(如同步复制或异步复制)。
- 切换:故障发生时,客户端或路由层(如 ProxySQL、HaProxy MySQL)负责将读写流量切换到数据一致的新节点,对应的是无共享存储的架构。
常见工具与平台
| 场景 | 推荐工具/技术 | 核心机制 |
|---|---|---|
| Web 服务器 | Nginx + Keepalived | Keepalived提供VIP(虚拟IP),Nginx检测upstream后端健康状态。 |
| 数据库 | MySQL InnoDB Cluster | Group Replication + MySQL Router,自动切换主节点。 |
| 消息队列 | RabbitMQ / Kafka 集群 | 主从选举 + 分区副本故障切换(Replica Reassignment)。 |
| 微服务 | Kubernetes (K8s) | Pod故障时自动重启,Node故障时Pod被调度到其他Node上。 |
| 传统高可用集群 | Pacemaker + Corosync | 操作系统级别的资源代理(RA),管理IP、挂载点、服务启动。 |
实现故障转移的三个关键要素
无论使用哪种技术,一套完整的故障转移方案都需要解决三个问题:
-
心跳检测
- 问题:如何判断节点是否真的挂了?(避免误判导致“脑裂”)
- 方案:
- 多路径心跳:使用双网卡、双交换机进行检测,避免单网卡故障导致误判。
- 仲裁机制:引入第三方组件(如K8s的etcd、ZooKeeper、Consul),由协调者根据多数投票结果决定是否切换。
-
数据一致性
- 同步模式:主节点写入后,必须等从节点确认后才返回成功(性能差,但绝对一致)。
- 异步模式:主节点写入并返回成功,然后异步发送给从节点(性能好,但可能丢数据)。
- 半同步模式:折中方案,至少一个从节点确认即可。
-
客户端透明切换
- 问题:即使服务端切换成功了,客户端的连接还是旧的,怎么让客户端知道服务器换了?
- 方案:
- VIP法:IP地址保持不变(从故障服务器漂移到备用服务器),客户端无感知。
- DNS法:修改DNS记录,指向新IP(生效慢,依赖DNS缓存)。
- 负载均衡器法:在LB层面监控后端,自动摘掉故障节点。
- 客户端自动重试:客户端SDK中内置重试逻辑和地址列表(如ZooKeeper、Eureka、Nacos等注册中心)。
如何设计一个简单的故障转移?
假设你有一个 Web 应用(Node.js/Java)和一台 MySQL 数据库,想实现基本的故障转移:
-
无状态应用层:
- 部署两台应用服务器。
- 前面架设一个 Nginx,配置
upstream指向这两台服务器,并启用健康检查。 - 在Nginx服务器上配置 Keepalived,提供VIP。
- 结果:任一一台应用服务器宕机,Nginx自动将流量切到另一台。
-
有状态数据库层:
- 部署两台MySQL:主(Master)、从(Slave)。
- 配置 半同步复制。
- 安装 MySQL Router 或 ProxySQL,它会监控主节点健康状态,在主节点挂掉时,自动将读写分离切换到新的主节点。
- 结果:主库宕机,从库被升级为新主,应用端通过路由器无缝切换。
一句话总结:无状态服务靠负载均衡器+健康检查+IP漂移;有状态服务靠主从复制+选举算法+连接路由器。