任何时候只允许一个节点对外提供写服务,必须通过第三方仲裁(如Ping网关、独立仲裁节点或共享存储租约锁)、强化复制一致性(GTID+半同步)、写入前校验主身份及失败隔离(STONITH或逻辑fencing)等机制强制保障。

核心原则:不允许多主同时写入
双主或主备架构下出现脑裂,本质是两个节点都自认为“主”,并同时接受写请求。数据库一旦双写,数据就不可逆地冲突、覆盖或丢失。所以防脑裂的第一条铁律是:**任何时候,只允许一个节点对外提供写服务**。这不能靠人工盯守,必须由机制强制保障。
引入第三方仲裁(Quorum)
双节点自身无法可靠判断对方是否真的宕机——网络抖动、短暂失联、进程卡死都可能被误判为“死亡”。因此必须依赖外部权威来裁决。常见且实用的仲裁方式有:
- Ping 网关或关键服务 IP:每个节点定期探测同一台稳定设备(如默认网关、DNS 服务器或核心 API 网关)。若本节点无法连通该 IP,说明自身网络已断,主动放弃主角色;仅当双方都连不通时,才需进一步处理(此时通常业务已整体不可达)。
- 使用独立仲裁节点(Arbiter):部署一台轻量级服务(如 etcd、Redis 或专用 HA 仲裁器),主备节点均向其上报心跳和状态。切换前必须获得仲裁节点确认“对方无响应且不可达”,否则拒绝升主。
- 基于共享存储的租约锁:在共享磁盘或分布式文件系统(如 NFS、CephFS)上维护一个带过期时间的锁文件。只有成功获取并续租该锁的节点,才被允许以主身份运行。失效即释放,避免单点故障导致长期锁死。
强化复制一致性与写入控制
即使网络正常,也要从数据库层堵住双写漏洞:
-
启用 GTID + 半同步复制:MySQL 8.0 中开启
gtid_mode=ON和rpl_semi_sync_master_enabled=1,确保主节点提交前至少一个从节点已接收并写入 relay log。这样即使发生切换,新主的数据也必然包含旧主的全部已确认事务,避免数据回退或跳变。 -
写入前校验主身份:应用层连接数据库前,先执行
SELECT @@read_only;或SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'read_only';。只对read_only=OFF的节点发起写操作,并配合中间件(如 ProxySQL、ShardingSphere)自动路由写请求。 -
禁止手动启停主库服务:所有主备切换必须经由 HA 工具(如 MHA、Orchestrator、Pacemaker)触发,禁用直接 systemctl restart mysqld。工具会在切主前强制原主设为
read_only=ON并等待复制追平,从源头阻断残留写入。
失败隔离(Fencing)作为兜底手段
当仲裁也无法达成一致时,必须有一方被“物理隔离”,防止其继续操作共享资源:
- STONITH(Shoot The Other Node In The Head):通过智能 PDU、IPMI 或云厂商 API,对疑似异常节点执行硬重启或断电。这是 Pacemaker/Corosync 集群的标准做法,但要求基础设施支持。
- 逻辑 fencing:若无法物理断电,可配置脚本自动清空本地 binlog、关闭 MySQL 实例、卸载共享磁盘挂载点,或修改防火墙规则屏蔽其数据库端口。虽不如 STONITH 彻底,但能大幅降低误写概率。
- 注意:绝不能仅靠“互相 kill”——比如两节点都尝试 ssh kill 对方 mysqld 进程。网络分区时它们会同时执行,结果是双杀失败,反而加剧混乱。

















