把 MySQL 的几个节点拉进同一个“群聊”,让它们实时同步事务、互相投票选出主节点,主节点出问题时大家自动重新选主,这就是 MGR(MySQL Group Replication)最直观的样子。很多人第一次听到“MGR”会误以为它是某个高可用中间件,其实它是 MySQL 官方内置的组复制方案,不需要额外部署独立中间件,直接靠插件和协议就能实现多节点之间的数据一致性与自动故障切换。
这篇内容是我自己多次在测试环境和生产环境里从零搭 MGR 的完整记录,包含参数解释、实际踩过的坑、故障排查思路,以及哪些场景下我建议你不要用 MGR。适合正在规划数据库高可用方案、被主从切换脚本折磨过、或者单纯想搞明白“组复制到底怎么玩”的朋友参考。
1. 整体设计拆解:为什么用“组队”的方式做高可用
1.1 MGR 与传统主从复制到底差在哪
传统 MySQL 主从复制其实是“单线联系”:主库写 binlog,从库拉取 binlog 回放,主挂了你得靠外部脚本、VIP 漂移或中间件去把某个从库提升为主库。这个过程最尴尬的是:主从之间数据延迟可能让你选出一个落后一大截的新主;多个从库之间也没法互相协商,很容易出现“你以为它当了主,它其实还差几个事务”的情况。
MGR 的思路则是把一组节点变成一个“对等群体”。节点之间通过组通信协议实时交换事务信息和成员状态,每个事务要不要接受,不是某个节点独自决定,而是组内根据多数派规则投票。这个模式天然解决了两件事:第一,事务冲突可以提前检测,减少双主写坏数据的问题;第二,节点故障可以通过组成员状态自动感知,不需要额外脚本去“试探”主库还活着没。
从架构上看,MGR 既保留了 binlog 复制的基本语义,又增加了分布式的成员管理和共识机制。它和传统异步复制不是替代关系,而是互补关系:MGR 适合做同城多机房的数据库高可用底座,传统复制则适合做读写分离、扩展读能力。
1.2 选型之前先泼盆冷水
MGR 虽然官方原生,但并不是万金油。我在实际评估里总结过一张“能不能用”的清单:
| 考虑维度 | 适合用 MGR | 不建议用 MGR |
|---|---|---|
| 节点规模 | 3 到 5 个节点 | 超过 7 个节点容易拖累组通信性能 |
| 写入并发 | 单主模式写入,多主模式低并发写入 | 高并发多主写入,冲突率会高到让你怀疑人生 |
| 网络质量 | 机房内网或同城专线,延迟稳定 | 跨地域、丢包严重、高延迟链路 |
| 表结构规范 | 所有表都有主键,无外键约束 | 大量无主键表、外键依赖严重 |
| 运维能力 | 有专门的 DBA,能处理分布式故障 | 只有一个开发兼职运维的场景 |
我把这张表放在最前面,是想让准备动手的人先想清楚:MGR 解决的是“节点故障后数据不丢、主库自动切换”的问题,它没有解决“业务写入模型冲突”的问题,更不是把 MySQL 变成无限水平扩展的分布式数据库。带着这个预期去搭,后面的每一步都会顺畅很多。
1.3 三个你必须先搞懂的核心概念
第一个是成员(member)。MGR 组里的每个 MySQL 实例都是一个成员,组内通过一个唯一的 UUID 标识自己,这个 UUID 就是group_replication_group_name。成员会互相广播心跳,某个成员多久没响应,其他成员就会把它标记为可疑状态,然后触发剔除流程。
第二个是单主模式与多主模式。单主模式(single-primary mode)下,组里只有一个节点可以写,其他节点自动变成只读副本;主节点出故障后,组内自动选一个新的主节点。多主模式(multi-primary mode)下,每个节点都可以写,但前提是你得接受“事务冲突可能让其中一个事务回滚”的现实。对于绝大多数业务,我建议先做单主模式,等你对冲突检测机制足够熟悉了,再考虑多主。
第三个是仲裁(quorum)。MGR 的故障判断依赖“组里多数派是否存活”。举个例子,3 个节点的组如果挂了 1 个节点,剩下 2 个仍然能形成多数派,可以继续选主和对外服务;但如果只有 2 个节点,挂掉 1 个,剩下 1 个无法独立形成多数派,整个组就会进入只读状态,无法自动选主。这也是我一直劝别人“至少上 3 节点”的原因,2 节点 MGR 不是高可用,是给自己找麻烦。
2. 环境准备与前置检查
2.1 版本与拓扑规划
MGR 从 MySQL 5.7.17 开始引入,到 5.7.27 之后逐步稳定,但我现在搭新环境只考虑 MySQL 8.0,原因很简单:8.0 对 MGR 的参数更友好,比如group_replication_ip_allowlist明确了访问控制,group_replication_exit_state_action可以自定义节点被驱逐后的行为,8.0.15 之后还支持了group_replication_consistency这样的强一致性控制能力。
拓扑上,我推荐的起步配置是 3 个节点:一个主库负责写,两个从库负责读和故障接管。三个节点可以部署在三台独立主机上,也可以部署在同一机房的三个虚拟机里,但千万不要把三个实例部署在同一台物理机的三个容器里做所谓“高可用”,因为物理机一挂全家升天,没有任何意义。
节点角色的分配也要提前规划好。MGR 没有传统意义上的“主从配置差别”,每个节点的 my.cnf 基础配置基本一致,区别只在于server_id、group_replication_local_address和端口。下面这张表是我常用的规划方式:
| 节点 | server_id | MySQL 端口 | MGR 通信端口 | 角色 |
|---|---|---|---|---|
| 节点 A | 1 | 3306 | 33061 | 初始种子节点 |
| 节点 B | 2 | 3306 | 33061 | 加入组 |
| 节点 C | 3 | 3306 | 33061 | 加入组 |
MGR 通信端口很容易被忽略。很多人只开放了 3306,结果START GROUP_REPLICATION一直报无法连接成员。MGR 的组通信引擎需要独立的端口来做节点间的消息交换,默认推荐 33061,你也可以换成别的端口,但要保证所有节点之间互相能通。
2.2 关键参数逐一解读
先看一份我常用的 MGR 基础配置模板,以节点 A 为例:
[mysqld] server_id = 1 gtid_mode = ON enforce_gtid_consistency = ON log_bin = binlog binlog_format = ROW binlog_checksum = NONE master_info_repository = TABLE relay_log_info_repository = TABLE relay_log_recovery = ON log_slave_updates = ON transaction_write_set_extraction = XXHASH64 plugin_load_add = 'group_replication.so' group_replication_group_name = "893a63e0-2a01-4f6c-9a11-5d8c5ac46a34" group_replication_start_on_boot = OFF group_replication_bootstrap_group = OFF group_replication_local_address = "10.10.10.11:33061" group_replication_group_seeds = "10.10.10.11:33061,10.10.10.12:33061,10.10.10.13:33061" group_replication_ip_allowlist = "10.10.10.0/24" group_replication_single_primary_mode = ON group_replication_enforce_update_everywhere_checks = OFF这些参数没有一个是多余的,我逐个说明:
server_id必须全局唯一,它参与成员标识和事务来源判断。
gtid_mode和enforce_gtid_consistency必须是 ON,MGR 底层完全依赖 GTID 来标识事务和做数据补偿。GTID 关闭时,节点之间无法对齐事务集合,也就谈不上组复制的数据一致性。
log_bin、binlog_format = ROW、log_slave_updates = ON这三个必须配套。MGR 在组成员之间传输的是行级变更信息,并且每个节点既要保存自己的 binlog,也要把从组里接收的事务继续写进自己的 binlog,这样才能让新加入的节点通过已有的 binlog 做增量恢复。
binlog_checksum = NONE这一点比较反直觉。默认的 binlog 校验和是 CRC32,但在 MGR 环境下传输事务时,校验和配置可能导致消息校验不一致,很多加入失败的问题都出在这里,所以官方推荐设置为 NONE。
master_info_repository = TABLE和relay_log_info_repository = TABLE把复制的元信息存到表里,而不是默认的 master.info 文件。MGR 节点之间有可能发生角色切换,使用表存储能更快、更可靠地恢复复制状态。
transaction_write_set_extraction = XXHASH64是 MGR 做事务冲突检测的基础。节点会把每行变更计算成 write set 的哈希值,传给组里其他节点做冲突比对。MySQL 8.0 选择 XXHASH64 是合理的,5.7 里这个参数默认是 OFF,忘了开会导致 MGR 无法识别事务冲突。
接着看group_replication系列参数:
group_replication_group_name是整个组的 UUID,所有成员必须完全一致。名字本身只用来标识组,不需要和网络地址对应。group_replication_start_on_boot = OFF是安全设置,避免 MySQL 进程一启动就自动尝试加入组、在错误网络状态下引起数据不一致。实际生产环境里我更推荐先用 OFF 手动控制加组时机,确认一切正常后再考虑改成 ON。group_replication_bootstrap_group = OFF用来防止节点误启动一个新组。只有第一个初始节点需要临时打开它,其他任何时候都保持 OFF。group_replication_local_address是当前节点在组内通信时对外暴露的地址和端口,必须唯一。group_replication_group_seeds是组内所有节点的通信地址列表,新节点加入时通过这些种子节点与其他成员建立连接。group_replication_ip_allowlist是组通信的访问白名单,只允许该网段内的成员参与组复制。旧版本里这个参数叫group_replication_ip_whitelist,8.0 改成了 allowlist,如果你参考老资料配置报错,多半就是参数名不一致。group_replication_single_primary_mode = ON表示使用单主模式,这是默认值,也是我推荐的首选。
2.3 初始化实例与前置检查
参数写好后,先不要急着启动组复制,先完成 MySQL 实例的初始化和基础检查。以我常用的方式,先初始化 MySQL 数据目录:
mysqld --defaults-file=/etc/my.cnf --initialize-insecure --user=mysql--initialize-insecure会生成一个 root 空密码的实例,方便第一次登录设置密码;生产环境建议用--initialize生成临时密码,再通过日志获取。初始化完成后启动 MySQL 服务,然后用 root 登录执行检查:
SHOW VARIABLES LIKE 'gtid_mode'; SHOW VARIABLES LIKE 'binlog_format'; SHOW VARIABLES LIKE 'transaction_write_set_extraction';重点确认gtid_mode已经是ON,如果当前是OFF_PERMISSIVE或ON_PERMISSIVE,需要通过SET GLOBAL gtid_mode = ON配合enforce_gtid_consistency逐步变更,不能直接在运行中的实例上跳级修改。
接下来创建组复制专用的复制账号。MGR 节点加入组时,需要通过一个复制用户从已有节点拉取数据,这个动作走的是异步复制通道,通道名固定为group_replication_recovery:
CREATE USER 'repl'@'%' IDENTIFIED BY 'YourStrongPassword'; GRANT BACKUP_ADMIN, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%';BACKUP_ADMIN权限是 MGR 自动恢复数据的必需项,少了它,新节点在拉取存量数据时会收到权限不足的错误。注意,MGR 要求所有节点都能用这个账号互相连接,所以主机范围建议设为%,或者至少覆盖三个节点的网段。
做完这些前置检查后,才开始真正进入组复制的搭建流程。我发现很多人在这一步就开始偷懒,不检查 GTID 直接启动,结果加入失败、数据补偿异常,最后又怪 MGR 不稳定,其实大部分是初始化姿势不对。
3. 实操全流程:从零拉起一个三节点 MGR 集群
3.1 启动第一个种子节点
所有准备工作做完,先用节点 A 启动组复制。第一个节点的作用是“创建”这个组,所以必须临时打开bootstrap_group开关。连接节点 A 后执行:
-- 只在第一个节点执行,目的是告诉组件“这个组由我创建” SET GLOBAL group_replication_bootstrap_group = ON; START GROUP_REPLICATION; SET GLOBAL group_replication_bootstrap_group = OFF;执行完第一句后要立刻执行第二句,不要长时间保持 bootstrap 开启,否则其他节点误启动时可能产生两个逻辑分组。启动后一般几秒内插件会完成组初始化,然后查询成员状态验证:
SELECT * FROM performance_schema.replication_group_members;这个结果里应该能看到节点 A 的MEMBER_STATE为ONLINE,MEMBER_ROLE为PRIMARY(单主模式下第一个节点自动成为主节点)。如果看到RECOVERING状态,不用慌,关键看它能否在几秒内转为ONLINE,如果一直停在RECOVERING,就要查复制通道和 binlog 情况,后面的故障排查部分会细说。
种子节点起来后,可以顺手做一个小测试:在节点 A 上建一张测试表、插入两条数据,然后查看节点 A 自己的 binlog 是否正常记录。这个测试不是为了验业务,而是确认基础复制链路没有在源头就出问题。
3.2 让第二个节点加入组
节点 B 需要先完成同样的配置文件和初始化流程,注意把server_id改成 2,group_replication_local_address改成节点 B 自己的通信地址。然后在节点 B 上执行一条特殊的变更指令,为group_replication_recovery通道配置复制账号信息:
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='YourStrongPassword' FOR CHANNEL 'group_replication_recovery';这条命令相当于告诉 MGR:“你加入组的时候,用这个账号去找组里的节点拉数据。”这里经常有人漏掉FOR CHANNEL 'group_replication_recovery',导致账号配置到了默认通道,MGR 启动时完全用不到。
然后启动组复制:
START GROUP_REPLICATION;节点 B 加入的过程大致是:向种子节点发起连接请求,组内成员经过验证后允许加入,节点 B 会先通过group_replication_recovery通道从某个已有成员拉取缺失事务,追平数据后再转成ONLINE。
等节点 B 状态变为 ONLINE 后,再回到节点 A 上查一下成员列表,你会看到两个 ONLINE 节点。此时可以测试一下自动故障切换的基础逻辑:手动停掉节点 A 的 MySQL 进程,等几秒后观察节点 B 是否自动变成 PRIMARY。如果你的配置正确,节点 B 会从 SECONDARY 自动提升为 PRIMARY,业务连接切换到节点 B 后照样可以写数据。
这里有个非常重要的实操心得:测试故障切换时,建议用kill -9模拟突发宕机,而不是在节点 A 上执行SHUTDOWN。因为正常关机流程会先优雅退出组,和真实宕机的成员消失过程不一样,测试结果会失真。
3.3 第三个节点加入与最终验证
节点 C 的加入流程和节点 B 完全一致,改好server_id和local_address,执行CHANGE MASTER配置恢复通道,再START GROUP_REPLICATION。三节点全部 ONLINE 后,在任意节点上执行:
SELECT * FROM performance_schema.replication_group_members; SELECT * FROM performance_schema.replication_group_member_stats\G第一个查询看整体状态,第二个查询可以看每个节点的事务提交情况。重点关注COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE这个字段,它表示还没在本地应用的事务队列长度,正常情况下应该接近于 0,如果一直在增长说明节点跟不上。
最终验证不要只停留在“成员状态都 ONLINE”,还要实际做数据同步验证:
-- 节点 A 写入 USE testdb; INSERT INTO t1 VALUES (1, 'hello'); -- 节点 B / C 查询 SELECT * FROM testdb.t1;单主模式下,节点 B 和 C 都应该是只读的,直接用 root 执行写入会报读只读错误,这是正常现象。业务读写分离架构里,把写流量切到 PRIMARY,把读流量分散到三个节点,就形成了一套完整的高可用集群。
3.4 多主模式切换的实操边界
如果你确实需要多主模式,可以在初始化时就把group_replication_single_primary_mode设置为 OFF,并把group_replication_enforce_update_everywhere_checks设置为 ON。但我不建议在已经跑了单主模式的集群上直接动态切换,官方虽然支持动态修改,但多主模式的冲突处理复杂度远高于单主,线上切换前一定要做完整的业务测试。
多主模式下最核心的操作法则是:不同节点写不同业务的数据分片。比如节点 A 只写订单表,节点 B 只写用户表,节点 C 只写商品表,这样事务冲突的概率最小;如果两个节点同时改同一行数据,必然会有一个事务被组内认证机制拦截并回滚,报错信息里通常会出现DEADLOCK相关提示。应用层必须对这类回滚有重试机制,否则用户就会直接看到异常。
还有个容易忽略的点:多主模式下,所有表必须有主键或者非空唯一键。MGR 的冲突检测依赖 write set 对行的唯一标识,没有主键的表会直接报错,拒绝执行 DDL。所以如果你的业务库里有大量老表没有主键,想上多主模式,前期改造工作可能比搭集群本身还大。
4. 常见问题与排查实录
4.1 节点一直处于 RECOVERING 状态
这是 MGR 搭建过程中遇到概率最高的问题。节点执行START GROUP_REPLICATION后,成员状态总是显示 RECOVERING,过一会儿甚至直接变成 ERROR,日志里出现“Member was expelled from the group”之类的信息。
排查路径我一般从三个方向展开:
第一,确认恢复通道的账号配置是否正确。检查视图:
SELECT * FROM performance_schema.replication_connection_status WHERE CHANNEL_NAME = 'group_replication_recovery'\G如果显示Last_IO_Error为访问被拒绝,基本可以确定是账号或者密码问题,回到CHANGE MASTER FOR CHANNEL 'group_replication_recovery'重新配置。
第二,确认节点之间能否互相连通。用 telnet 或 nc 从节点 B 测试到节点 A 的 33061 端口:
nc -zv 10.10.10.11 33061不通的话检查防火墙和安全组规则。组复制需要 3306 用于表数据同步恢复,需要 33061 用于组成员通信,两台都别漏。
第三,确认网络白名单是否覆盖了所有节点。group_replication_ip_allowlist如果写得太窄,比如只写了节点 A 的 IP,节点 B 加入时会被拒绝。这类问题日志里会明确提示 “not in the allowlist”,一眼就能定位。
4.2 双节点组宕机一个后整个组不可写
这个问题不是 MGR 的 Bug,而是仲裁设计使然。2 个节点的组如果挂了一个,剩下一个节点无法确认自己是否可以继续成为多数派,组会主动进入只读保护状态。有些资料把 2 节点 MGR 包装成“低成本高可用”,但在多数派机制下,它本质上没有高可用意义。
我实际配置过的最小可用集群一定要 3 个节点。如果资源实在紧张,退而求其次的做法是用 2 个数据节点加 1 个无数据的仲裁节点,但 MGR 并没有官方独立仲裁节点概念,仲裁节点也参与事务认证,配置成本并不比普通节点低多少,所以我一般不建议这么折腾。
另外,关于网络分区场景,马脑裂这个词在 MGR 里不对等。MGR 不是通过选主算法直接产生新主,而是当一个节点发现自己在组内被隔离时,会进行“离开组”的判断,无法形成多数派的少数分区会主动降级为只读或退出,从设计上避免两个分区同时提供写入服务的脑裂场景。但前提是网络分区必须真实发生且超时逻辑正常,如果网络抖动导致节点被误判,那就是另一些参数调优的坑了。
4.3 恢复被驱逐的节点
当某个节点因为长时间通讯超时被组驱逐后,MEMBER_STATE会变成 ERROR,此时这个节点默认已经不能再重新加入组,必须要先手动停掉组复制,清掉成员身份信息,再重新加入。
我常用的恢复流程是:
STOP GROUP_REPLICATION; RESET SLAVE ALL FOR CHANNEL 'group_replication_recovery'; START GROUP_REPLICATION;如果这一步失败,报错说节点已经被组保留着旧身份,可以在 MySQL 数据目录下删除gr_member_list相关文件(实际文件名可能是gr_*开头的文件),再重启实例重试。生产环境执行删除操作前务必确认数据没有未同步的差异,最好先用mysqldump做一次逻辑备份。
这里分享一个规避恢复麻烦的技巧:MySQL 8.0 支持group_replication_exit_state_action,把它设置为READ_ONLY,节点被驱逐后进入只读状态而不是直接 ERROR,保留重新加入组的可能性。这比默认行为温和很多,适合线上环境。
与之配套的还有group_replication_member_expel_timeout,默认值很小,如果在网络短暂抖动时不想让节点被立刻驱逐,可以调大这个超时时间。我见过有些维护窗口里重启交换机导致所有节点同时闪断,如果 expel timeout 太短,整组会乱成一锅粥。
4.4 多主模式下的事务冲突与死锁
多主模式上线后,业务最常见的问题是并发更新同一行时,其中一个事务返回死锁错误。从 MGR 的角度看,这不是真正的数据库死锁,而是认证阶段发现两个事务修改了相同行,组内投票否决了后到的事务。被否决的事务会立即回滚,应用层拿到的是类似DEADLOCK的报错。
我处理这类问题的方法分三步:第一步,通过performance_schema查冲突事务的具体表和对象;第二步,和业务对齐写入路由策略,尽量让同一行数据的写入固定落在同一个节点;第三步,如果冲突依然频繁,就要考虑拆分表或拆分业务,而不是继续加大多主节点数。多主模式节点越多,事务在所有节点之间广播和认证的开销也越大,无限制扩展只会让性能更差。
对绝大多数内部管理系统、电商后台、运营平台来说,单主模式的 MGR 已经能提供足够的高可用能力。强烈不建议为了“多个节点都能写”的炫技需求,把线上业务拖进多主冲突的泥潭里。
5. 运维监控与后期扩展经验
5.1 日常健康检查要盯哪些指标
MGR 的日常运维和传统主从不一样,它的状态是动态变化的,不能只看SHOW SLAVE STATUS那套指标。我每次巡检都会固定执行下面四类检查:
第一,成员在线情况。查询performance_schema.replication_group_members,确认所有节点状态都是 ONLINE,且没有多余节点处于 RECOVERING 或 ERROR。
第二,事务堆积情况。查询performance_schema.replication_group_member_stats,关注每个节点COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE是否持续增长。如果某个节点积压事务越来越多,说明它的硬件性能或磁盘 IO 成了瓶颈。
第三,主节点身份确认。确认当前 PRIMARY 和业务配置的写入端点一致。MGR 虽然会自动选主,但应用层的连接池不一定自动刷新,很多故障时的“无法写入”其实是因为应用还在连旧主。我建议在业务接入层做一层探活,定期探测当前 PRIMARY,动态切换连接目标。
第四,节点间网络延迟。MGR 对网络延迟敏感,如果节点间 RTT 长期超过 10 毫秒,事务提交耗时和故障感知时间都会明显恶化。巡检时可以在节点之间跑ping统计丢包率和延迟,偏差明显的要及时找网络团队处理。
5.2 在线扩容与缩容操作
MGR 比传统主从更容易做在线扩容。新节点准备完成后,执行START GROUP_REPLICATION,它会自动从组内同步存量数据和增量数据,追上后转为 ONLINE。扩容后单主模式下的新节点默认是 SECONDARY,只承担读流量。
缩容则要更小心。先把节点从组里停掉:
STOP GROUP_REPLICATION;停掉后该节点不会影响其他节点继续工作,如果配置了start_on_boot = OFF,它重启后也不会自动加回组。缩容后记得同步更新其他节点 my.cnf 里的group_replication_group_seeds,把离开节点的地址移除,避免后续新节点加入时尝试连接无效地址导致等待超时。
5.3 备份恢复策略要重新设计
MGR 环境下的备份不能再用传统“从库备份”的思路。因为所有节点都是组成员,任何一个节点的物理备份都可以恢复整个组的数据,但恢复时必须注意 GTID 集合的完整性。我推荐用mysqldump --single-transaction --set-gtid-purged=ON做逻辑备份,恢复时让它保留原 GTID,这样恢复出来的实例可以直接作为新成员加回原组,避免出现 GTID 空洞导致的数据补偿异常。
物理备份方面,如果用了 MySQL 8.0 的 clone 插件做全量备份,恢复后同样要检查 GTID 集合。曾经有个项目在恢复备份后直接START GROUP_REPLICATION失败,就是因为恢复数据里缺少某些事务的 binlog,被组内成员判定为无法补齐,最后只能重建节点。
5.4 几个让我印象深刻的实践教训
最后分享几个靠踩坑换来的经验。
第一个是“不要在生产环境随意开关group_replication_bootstrap_group”。有一次维护窗口里,我在节点 A 上重启实例后忘记检查参数,发现应用写入全部超时,一查才发现因为start_on_boot配置成了 ON,加上 bootstrap 开关被误设,节点重启后自己拉起了一个只有自己的新组,和其他节点彻底失联。这种事故一旦发生,恢复起来非常狼狈,数据一致性要花很长时间去核对。所以我现在的原则是:生产环境bootstrap_group永远保持 OFF,只有明确要重建组的维护窗口才临时打开,用完立刻关掉。
第二个是“单主模式下不要忽视只读节点上的大查询”。MGR 的数据同步和查询共用同一个实例资源,如果某个 SECONDARY 节点上跑了一个全表扫描慢查询,它会拖累该节点应用组内事务的速度,最终表现为整个组提交延迟上升,主节点也受影响。所以要给只读业务单独划分资源,不要把所有查询都压在同一组节点上。
第三个是“应用层切换逻辑一定要做”。MGR 实现了数据库层的自动选主,但应用层如果始终连接固定 IP,那么主库切换后应用仍然会把写流量打到新的 SECONDARY 上,然后被只读保护拦住。我见过很多团队说“MGR 不稳定”,最后排查发现是应用层没有感知主节点变化。这不是 MGR 的问题,是你还缺一层服务发现。用代理中间件、VIP 或者应用内置的 MGR 探活逻辑,把主节点变化通知到写入端,才算真正闭环。
如果你刚开始接触 MGR,我建议先用三台虚拟机搭一个最小的单主集群,把加节点、杀节点、恢复节点这几个动作反复练几遍,然后再考虑上生产。高可用方案从来不是部署完就结束,而是要通过一次次故障演练,让你在真正出问题时能不慌不乱。我个人在这条路上踩过的坑,基本上都写在上面了,希望你能少走几步弯路。