14_MySQL 高可用与容灾架构 ------ MGR / InnoDB Cluster / Keepalived+VIP / 双主 / 读写分离 / 两地三中心
系列定位 :《MySQL 数据库全栈运维实战系列》第 14 篇,与《Oracle 数据库全栈运维实战系列(VMware 版)》同构对标(Oracle 14 高可用容灾:RAC + DG)。适用版本 :MySQL 8.0(5.7 差异随文标注)。官方文档:MySQL 8.0 Reference Manual、Group Replication / InnoDB Cluster / MySQL Router 章节。
衔接说明 :第 13 篇已在 VMware 虚机上完成 GTID 主从复制与 InnoDB Cluster 双节点部署(系列核心篇)。本篇不再重复部署细节,而是把已部署的组件体系化:从"能用的主从"升级为"可切换、可读写分离、可容灾"的高可用架构,并与 Oracle RAC / DG 的能力做同编号对照。
一、高可用与容灾全景(一张图看懂)

图 1
| 层次 | 组件 | 类比 Oracle | 解决的问题 |
|---|---|---|---|
| 接入层 | Keepalived+VIP / MySQL Router / ProxySQL | SCAN VIP / Listener 故障切换 | 应用连哪个地址、主挂了自动切 |
| 同城高可用 | InnoDB Cluster(MGR 单主) | RAC 节点 | 实例级故障、秒级自动选主 |
| 数据复制 | 组复制 vs 异步复制 | RAC Cache Fusion vs DG Redo 传输 | 强一致 vs 异地容灾 |
| 异地容灾 | 异地灾备库(异步复制) | Data Guard | 机房级故障、城市级灾难 |
核心差异 :Oracle 用 RAC(共享存储 + Cache Fusion)做同城高可用,用 Data Guard(Redo 传输)做异地容灾;MySQL 没有共享存储集群 ,高可用全部基于复制实现------MGR 提供强一致组复制(对标 RAC 的"节点故障自动切换"),异地 binlog 异步复制对标 DG。
二、高可用方案选型对比
| 方案 | 一致性 | 切换方式 | RTO | 适用规模 | 类比 Oracle |
|---|---|---|---|---|---|
| 异步主从(GTID) | 最终一致 | 手动 / 脚本 | 分钟级 | 读多写少、可接受少量延迟 | DG 最大性能模式 |
| 半同步复制 | 接近强一致 | 手动/脚本 | 秒级~分钟级 | 数据不能丢的场景 | DG 最大可用模式 |
| Keepalived+VIP 双主/主从 | 依赖底层复制 | VRRP 自动漂移 | 秒级 | 中小规模、单机主从 | Listener 切换 |
| MGR 单主(InnoDB Cluster) | 强一致(Paxos) | 自动选主 | 秒级 | 中大规模、高可用核心 | RAC 节点故障切换 |
| MGR 多主 | 强一致(冲突检测) | 无主概念 | 秒级 | 极少用(写冲突风险) | --- |
| 两地三中心 | 同城强一致 + 异地异步 | 同城自动、异地手动 | 同城秒级/异地分钟级 | 金融级 | RAC + DG 组合 |
选型结论 :生产核心库优先 InnoDB Cluster(MGR 单主) ;对成本敏感且可接受手动切换的,用 GTID 主从 + Keepalived+VIP ;异地容灾一律在 MGR/主从基础上额外挂一个异地异步从库(MGR 组内不做跨城,跨城用异步复制)。
三、环境准备(VMware 虚机)
| 节点 | 角色 | 建议配置 | 用途 |
|---|---|---|---|
| db1 / db2 / db3 | MGR 三节点 | 4C8G / 100G | 同城集群(至少 3 节点才能容忍 1 节点故障) |
| dr1 | 异地灾备 | 4C8G / 100G | 异地异步从库(另一 VMware 宿主/数据存储) |
| 接入节点 | MySQL Router / ProxySQL / Keepalived | 2C4G | 可与应用同机或独立 |
VMware 注意事项 (高可用专题重点):1. 节点必须分布在不同宿主机/数据存储 :MGR 三节点若跑在同一 ESXi 宿主机上,宿主机宕机 = 集群全灭,失去高可用意义。生产演练至少保证 db1/db2 在不同 ESXi。2. 时间同步强制 :组复制对时间敏感,所有虚机必须开 VMware Tools 时间同步(或配置 chrony/NTP 指向同一 NTP),时间偏差过大会导致选主异常、GTID 混乱。3. 网卡模式与 IP :Keepalived 的 VIP 需要桥接/仅主机网段支持二层广播(VRRP 多播 224.0.0.18),NAT 模式下 VRRP 多播可能不通,建议用桥接或自定义网段。4. 磁盘控制器/IO :MGR 三节点并发写盘,建议厚置备(thick eager zeroed)或 SSD 直通,避免 thin 置备在写放大时抖动影响组提交。5. 快照回滚坑 :对已加入 MGR 的节点做 VMware 快照再回滚,会导致 GTID 位点与组内不一致,必须
RESET MASTER后重新clone加入,切勿直接回滚快照当恢复手段。6. 内存预留:生产建议给虚机预留全部内存(Reservation),避免 ballooning 导致 MySQL 被回收触发组复制超时。
# /etc/my.cnf(MGR 三节点公共配置,差异项用 server_id 区分)
[mysqld]
server_id = 1 -- 解释:每个节点唯一,db1=1 db2=2 db3=3
gtid_mode = ON -- 解释:MGR 强制开启 GTID(对标 Oracle DG 强制归档)
enforce_gtid_consistency = ON
log_bin = mysql-bin
binlog_format = ROW -- 解释:组复制要求 ROW
binlog_row_image = FULL
log_replica_updates = ON -- 解释:8.0 从库也要写 binlog(5.7 为 log_slave_updates)
transaction_write_set_extraction = XXHASH64 -- 解释:写入集提取,冲突检测依赖
loose-group_replication_group_name = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" -- 解释:必须是合法 UUID,全组一致
loose-group_replication_local_address = "192.168.10.51:33061" -- 解释:组通信地址,每节点改 IP
loose-group_replication_group_seeds = "192.168.10.51:33061,192.168.10.52:33061,192.168.10.53:33061"
loose-group_replication_start_on_boot = OFF -- 解释:先手动启动,避免开机自动加入造成脑裂
loose-group_replication_ single_primary = ON -- 解释:单主模式(8.0 实际参数名为 group_replication_single_primary_mode)
四、MGR 单主集群原理与实操(官方:Group Replication)
4.1 组复制选主流程

图 2
关键约束 :MGR 需要多数派 (3 节点容忍 1 节点故障;5 节点容忍 2 节点故障)。2 节点集群不满足容错------挂任何一个都凑不齐多数派,集群整体只读,这是与 Oracle RAC(2 节点可正常工作)的重要差异。
4.2 启动组复制(节点 1)
-- ① 安装插件(解释:group_replication 插件只需装一次)
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
-- ② 创建复制账号(解释:组复制内部通信账号,全组一致)
SET SQL_LOG_BIN = OFF;
CREATE USER 'rpl_user'@'%' IDENTIFIED BY 'Rpl@Pass2026';
GRANT REPLICATION SLAVE ON *.* TO 'rpl_user'@'%';
SET SQL_LOG_BIN = ON;
-- ③ 配置单主引导(解释:仅主节点执行,其余节点不执行 BOOTSTRAP)
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
4.3 从节点加入(节点 2、3)
-- ① 同样安装插件、建账号(解释:账号密码与主节点一致)
INSTALL PLUGIN group_replication SONAME 'group_replication.so';
-- ② 用 GTID 自动定位复制源(解释:8.0 推荐 CHANGE REPLICATION SOURCE 语法,5.7 为 CHANGE MASTER)
CHANGE REPLICATION SOURCE TO
SOURCE_USER='rpl_user',
SOURCE_PASSWORD='Rpl@Pass2026',
SOURCE_HOST='192.168.10.51',
SOURCE_PORT=3306,
SOURCE_AUTO_POSITION=1; -- 解释:GTID 自动位点,无需手动 file/pos
-- ③ 启动组复制(解释:不做 BOOTSTRAP,直接加入已有组)
START GROUP_REPLICATION;
4.4 验证组状态
-- 查看集群成员(解释:performance_schema.replication_group_members 是 MGR 第一视图)
SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
-- 预期输出:3 行,主为 PRIMARY,其余为 SECONDARY,状态均为 ONLINE
-- 查看主库当前可写(解释:单主模式下仅 PRIMARY 接受写)
SHOW STATUS LIKE 'group_replication_single_primary_mode';
SHOW STATUS LIKE 'group_replication_primary_member';
五、InnoDB Cluster:MGR + MySQL Shell + MySQL Router(官方:InnoDB Cluster)
InnoDB Cluster = 用 MySQL Shell 的
dbaAPI 把上述 MGR 集群托管化 ,再用 MySQL Router 做自动连接路由------这是官方推荐的高可用成品方案,对标 Oracle RAC + SCAN 服务。

图 3
| 端口 | 用途 | 类比 Oracle |
|---|---|---|
| 6446 | 读写连接(自动路由到当前主) | SCAN 服务(读写) |
| 6447 | 只读连接(路由到 Secondary) | 只读服务 |
| 64460 | Router 元数据 | --- |
# ① MySQL Shell 创建集群(解释:在主库上执行,相当于把 MGR 封装成 Cluster)
mysqlsh --uri root@192.168.10.51:3306
# JS 模式下:
# var cluster = dba.createCluster('prodCluster');
# cluster.addInstance('root@192.168.10.52:3306'); -- 自动 clone 数据后加入
# cluster.addInstance('root@192.168.10.53:3306');
# ② 引导 MySQL Router(解释:--bootstrap 自动从集群元数据读拓扑)
mysqlrouter --bootstrap root@192.168.10.51:3306 --directory=/usr/local/mysqlrouter
# ③ 启动 Router
mysqlrouter -c /usr/local/mysqlrouter/mysqlrouter.conf &
故障切换自动生效 :主库宕机后,MGR 自动选新主,MySQL Router 每隔几秒从元数据刷新拓扑,把 6446 的新连接路由到新主。应用无需改连接串------这正是它对标 SCAN VIP 的核心价值。
-- 验证集群状态(解释:mysqlsh 里执行 cluster.status() 等价查询)
SELECT * FROM mysql_innodb_cluster_metadata.clusters\G
六、Keepalived + VIP 方案(轻量主从,对标 Listener 切换)
对未上 InnoDB Cluster 的中小主从,用 Keepalived 给主库挂一个 VIP,主库宕机时 VIP 自动漂到从库。

图 4
# /etc/keepalived/keepalived.conf(主库)
vrrp_script chk_mysql {
script "/etc/keepalived/check_mysql.sh" # 解释:检测 3306 是否存活
interval 2
weight -20
}
vrrp_instance VI_1 {
state MASTER # 解释:从库填 BACKUP
interface ens33
virtual_router_id 51
priority 100 # 解释:从库 90
advert_int 1
virtual_ipaddress {
192.168.10.100 # 解释:应用连接的固定 VIP
}
track_script { chk_mysql }
}
# /etc/keepalived/check_mysql.sh(解释:mysql 探测失败则降优先级触发漂移)
#!/bin/bash
mysql -uroot -p"$MYSQL_PWD" -e "SELECT 1" >/dev/null 2>&1 || exit 1
exit 0
坑提醒 :VIP 漂移只是把 IP 切过去,从库此时未必已提升为主 (只读)。生产上必须在
notify_master脚本里自动STOP REPLICA; RESET MASTER;提升从库为可写,否则应用连上的是只读库。这个"VIP 漂了但库还只读"是 Keepalived 方案最常见的事故。
七、读写分离架构

图 5
| 实现方式 | 原理 | 适用 |
|---|---|---|
| 应用层路由 | 代码里 master/slave 数据源 | 简单、耦合代码 |
| MySQL Router | 6446 写 / 6447 读 | 官方、零业务侵入 |
| ProxySQL | 基于规则路由(见第 16 篇) | 复杂规则、缓存、审计 |
-- 从库只读设置(解释:读写分离下从库必须只读,防止误写)
SET GLOBAL super_read_only = ON; -- 8.0:连 SUPER 权限也禁止写
SHOW VARIABLES LIKE 'read_only';
复制延迟问题:读写分离最大的坑是"刚写主库、立刻读从库读不到"。方案:① 关键事务后强制走主库读(hint);② 监控复制延迟,延迟大时把该从库从读池摘除(见第 17/19 篇告警);③ 半同步复制降低延迟窗口。
八、两地三中心(金融级容灾)

图 6
| 中心 | 角色 | 复制方式 | 恢复目标 |
|---|---|---|---|
| 主数据中心 | 生产 | MGR 强一致 | 实例故障秒级切换 |
| 同城备份中心 | 同城容灾 | 组内成员 / 半同步 | 机房故障 RTO 分钟级 |
| 异地灾备中心 | 异地容灾 | 异步 binlog | 城市级灾难 RTO 小时级、RPO 秒~分钟 |
MGR 不建议直接跨城 :组通信跨城市延迟(>5ms)会拖慢组提交、降低吞吐。跨城一律用异步复制单独维护一个异地从库,平时只读/备份,灾难时手动提升(对标 Oracle DG 切换需要 DBA 介入)。
-- 异地灾备库配置(解释:异地从库挂在主库下做异步复制,与 MGR 组独立)
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='192.168.10.51',
SOURCE_USER='rpl_user',
SOURCE_PASSWORD='Rpl@Pass2026',
SOURCE_AUTO_POSITION=1;
START REPLICA;
九、高可用切换演练(验收动作)
-- ① 模拟主库宕机(在主库)
-- mysqladmin -uroot shutdown
-- ② 在任一存活节点查新主(解释:30 秒内自动选出新 PRIMARY)
SELECT MEMBER_HOST, MEMBER_ROLE, MEMBER_STATE
FROM performance_schema.replication_group_members
WHERE MEMBER_ROLE = 'PRIMARY';
-- ③ 验证新主可写、旧主恢复后可重新加入
-- 旧主重启后:START GROUP_REPLICATION;(自动追赶并作为 SECONDARY 加入)
十、要点速记
- MySQL 没有共享存储集群,高可用全部基于复制:MGR(强一致)+ 异地异步(容灾)。
- MGR 至少 3 节点才能容忍 1 节点故障,2 节点凑不齐多数派、故障即只读。
- InnoDB Cluster = MGR + MySQL Shell + MySQL Router,6446 写 / 6447 读,应用零改连接串(对标 SCAN)。
- Keepalived+VIP 只是漂 IP,必须配合脚本把从库提升为可写,否则漂过去是只读库。
- 两地三中心:同城用 MGR 强一致、异地用异步 binlog;MGR 不直接跨城(网络延迟拖慢组提交)。
- VMware 上高可用的前提:节点分散到不同 ESXi、NTP 同步、VRRP 二层网络、禁止对集群节点直接快照回滚。
十一、踩坑记录(实测汇总)
| # | 坑 | 现象 | 解决 |
|---|---|---|---|
| 1 | 2 节点 MGR | 挂一台后整组只读 | 扩到 3 节点,保证多数派 |
| 2 | 节点都在同一 ESXi | ESXi 宕机全组灭 | 节点分散到不同宿主机 |
| 3 | 时间不同步 | 选主异常、GTID 混乱 | 开 chrony + VMware Tools 时间同步 |
| 4 | VRRP NAT 网段不通 | VIP 不漂移 | 改桥接/自定义网段,放行 224.0.0.18 |
| 5 | VIP 漂了但从库只读 | 应用连上只读库报错 | notify_master 脚本里自动提升从库 |
| 6 | log_replica_updates 没开 | 从库无法加入组复制 | 8.0 开 log_replica_updates(5.7 log_slave_updates) |
| 7 | 直接 VMware 快照回滚节点 | GTID 与组内不一致 | RESET MASTER 后重新 clone 加入 |
| 8 | 异地从库挂在组内 | 跨城延迟拖垮组提交 | 异地走独立异步复制,不进组 |
| 9 | read_only 忘开 | 误写从库 | 从库统一 super_read_only=ON |
| 10 | 内存被 ballooning | 组复制超时断连 | 虚机预留全部内存 |
| 11 | group_replication_local_address 写错端口 | 组通信起不来 | 确认 33061 组通信端口互通 |
| 12 | 多主模式写冲突 | 同行并发写报错 | 生产用单主,多主仅特殊场景 |
十二、参考资料(官方正式文档)
| 主题 | 官方链接 |
|---|---|
| MySQL 8.0 Reference Manual | https://dev.mysql.com/doc/refman/8.0/en/ |
| Group Replication | https://dev.mysql.com/doc/refman/8.0/en/group-replication.html |
| InnoDB Cluster | https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-innodb-cluster.html |
| MySQL Router | https://dev.mysql.com/doc/mysql-router/8.0/en/ |
| Replication(异步/半同步) | https://dev.mysql.com/doc/refman/8.0/en/replication.html |
| GTID 格式与配置 | https://dev.mysql.com/doc/refman/8.0/en/replication-gtids.html |
本文章属于《MySQL 数据库全栈运维实战系列》高可用架构篇。欢迎收藏、交流。