资讯动态

MySQL MGR单主高可用集群部署工程实践与故障速查

发布时间:2026/10/28 3:34:36 来源:尧图企业网站定制
1. 为什么把 MySQL MGR 单主高可用集群部署当作一套完整工程来做MySQL MGR 单主高可用集群部署这件事我在几个不同规模的业务里都做过从三台 4C8G 的小集群到八节点的核心库都折腾过。先把话说清楚MGR 全称 MySQL Group Replication是官方在 5.7.17 之后内置的组复制方案单主模式指的是组内只有一个节点接受写入其余节点以只读身份同步数据主节点挂掉之后组内自动选举出新主。它解决的是传统一主多从方案里那几个老大难问题——切主靠外部脚本、异步复制丢数据、脑裂后不知道该信谁。适合谁来读手里已经有一主两从、被手工切主折磨过的 DBA正在做新系统选型、需要金融级数据一致性但不想上商业数据库的架构同学还有一类是刚开始接触复制协议、想搞明白多数派到底怎么算的开发者。我见过太多团队把 MGR 当成配置项改一改就能跑的东西结果上线三个月一次网络抖动触发了自动驱逐紧接着自动重加入失败从节点全掉线最后靠人工拉起。问题不在于 MGR 不稳定而在于部署阶段就少考虑了好几层东西节点角色怎么摆、组通信地址和业务地址要不要分离、流控阈值设多少、切主之后应用连接池怎么办。这些都不是启动插件那两行命令能覆盖的。所以这篇东西我按一个完整工程来写从选型理由到参数为什么这么填到三节点从零搭起来到真正演练一次主库宕机最后把踩过的坑整理成速查表。文中出现的 IP、UUID、密码全部是示例值你照着自己的环境替换即可。我不会只给你一堆命令重点是每个参数背后在权衡什么这样你遇到我没写到的场景也能自己判断。2. 方案选型MGR 单主到底替你解决了哪些真问题2.1 从异步主从到组复制痛点到底迁移到了哪里传统一主两从的本质是单向日志搬运。主库写完 binlog 就返回成功从库什么时候追上完全看网络和负载这就天然带着两个隐患主库磁盘坏了最后几百个事务可能还没传出去主从延迟几秒的时候做读写分离用户刚下单查不到订单。半同步复制semi-sync缓解了第一点但它依然依赖一个主的角色主挂了还是要靠外部中间件或者脚本去判断该切谁判断过程本身就是风险源。MGR 换了个模型它不再有谁是权威的先验假设而是让每个节点都持有一份成员视图写入需要经过组内多数派认证certify之后才提交。这个认证过程本质上是检查事务的写集合writeset有没有和并发事务冲突冲突了就回滚没冲突就广播出去。单主模式下因为只有一个节点产事务冲突几乎不会发生认证器压力小所以单主的吞吐普遍比多主稳。代价是每一次提交都要等组内通信走一轮网络往返RTT直接叠在事务延迟上。我实测下来同机房内网 RTT 在 0.2ms 左右的三节点集群单条小事务的提交延迟比单机模式多 1ms 上下批量插入时因为能攒批次这个比例会被摊薄。跨机房部署就要谨慎了两地 RTT 到 5ms再叠加认证和广播写入延迟肉眼可见变差这时候更该考虑的是同城三副本 异地异步从库的混合结构而不是硬把 MGR 拉到跨地域。2.2 单主模式与多主模式的取舍逻辑MGR 两种模式都能开我几乎不推荐多主。原因很朴素多主意味着多个节点同时写写入冲突要靠认证器回滚回滚就意味着业务层收到莫名其妙的死锁或者主键冲突报错而且热点行更新场景下回滚率不低。多主真正舒服的场景是每个节点只写自己分片的数据可一旦你的业务做不到完美分片多主就是给自己找麻烦。单主模式的读扩展依然保留从节点可以承接只读流量只是要注意一致性级别。默认情况下从节点应用是异步的读到的可能是旧数据。MySQL 8.0 提供了group_replication_consistency参数可以按会话粒度设置取值行为适用场景EVENTUAL默认不做任何等待日志、报表、非关键查询BEFORE_ON_PRIMARY_FAILOVER新主上任时先追平旧主已提交事务再放行想减少切主瞬间的读旧数据BEFORE读之前先等所有已提交事务在本节点应用完读己之写要求强的查询AFTER事务提交后等全组应用完才返回写后立刻跨节点读BEFORE_AND_AFTER两头都等强一致但延迟最高我的建议是全局保持 EVENTUAL只在少数关键业务的事务里用SET SESSION group_replication_consistency BEFORE做局部加强。把全局设成 BEFORE_AND_AFTER 等于把集群变成一个同步复制系统吞吐掉一半以上得不偿失。2.3 三节点起步的容量与容错计算MGR 的容错能力由多数派决定。组内 N 个节点能容忍floor((N-1)/2)个节点故障。所以三节点容忍 1 台五节点容忍 2 台。两节点是没有意义的因为任意一台挂了都凑不齐多数派整个组直接停止服务。这就带来一个容量问题三节点集群里任意时刻最多一台故障剩下的两台要扛住全量写入。所以规划时不能按三台均摊来算得按两台扛全量预留 CPU 和 IO。我一般会让每台机器的规格做到预估峰值的 60% 以上容量也就是三台合起来至少有 1.8 倍的冗余。磁盘同理binlog 保留 7 天的话按峰值写入量乘以 7 天再乘 1.5 的系数留空间。还有一个常被忽略的点MGR 的组通信端口默认 33061和数据端口是分开的。这两个端口必须双向互通且不能经过任何会改写包内容的中间层。曾经有个环境把 33061 挂在了某个四层转发后面表面上通实际上组内心跳被拆散节点反复被驱逐查了两天才定位到。3. 部署前的规划主机、端口和那几个不能省的参数3.1 主机、目录与端口的三层规划我一般把规划做成表格落到文档里避免三台机器配置不一致导致两台能起来一台起不来。项目节点 A节点 B节点 C说明业务地址10.10.0.1110.10.0.1210.10.0.13应用连接用组通信地址10.10.0.11:3306110.10.0.12:3306110.10.0.13:33061仅组内使用server_id111213全组唯一数据目录/data/mysql/data同左同左独立磁盘初始角色主从从引导时指定server_id一定要规划不要随手写 1、2、3因为很多团队会把这个集群和另一个集群混在一套监控里重复的 server_id 会让 binlog 分析和慢日志聚合彻底混乱。我用机房编号 序号的方式比如 11、12、13。组通信地址和业务地址是否分离看规模。小集群直接复用同一张网卡也没问题但要在group_replication_ip_allowlist里写清楚允许的网段默认值只允许 localhost不改的话节点之间根本连不上报错信息还挺隐晦——通常表现为START GROUP_REPLICATION卡住然后超时。3.2 配置文件里每一项都在防什么这是我实际在用的配置模板8.0.2x 到 8.0.3x 都适用个别版本参数名有差异我会标出来。[mysqld] server_id11 port3306 datadir/data/mysql/data socket/data/mysql/mysql.sock pid_file/data/mysql/mysql.pid log_error/data/mysql/error.log # 复制基础 gtid_modeON enforce_gtid_consistencyON log_binbinlog binlog_formatROW binlog_row_imageFULL log_replica_updatesON binlog_expire_logs_seconds604800 # 组复制 plugin_load_addgroup_replication.so group_replication_group_name6b1f4d2e-9c3a-11ee-8b6f-0242ac120003 group_replication_start_on_bootOFF group_replication_local_address10.10.0.11:33061 group_replication_group_seeds10.10.0.11:33061,10.10.0.12:33061,10.10.0.13:33061 group_replication_bootstrap_groupOFF group_replication_single_primary_modeON group_replication_enforce_update_everywhere_checksOFF group_replication_ip_allowlist10.10.0.0/24 group_replication_autorejoin_tries3 group_replication_member_expel_timeout10逐条解释几个容易被抄错的group_replication_group_name必须是合法的 UUID 格式三台机器填完全一样的值。它不是集群名字而是这个组的身份标识写错了会变成两个独立的组谁也看不见谁。生成方式随便找个 UUID 生成器别自己编十六进制。group_replication_start_on_bootOFF是有意为之。如果设成 ON机器重启后 MySQL 会自动尝试加入组这时候如果多数派还没起来节点会反复重试并刷大量错误日志。我倾向于先让实例起来、确认基础复制通道没问题再手工执行START GROUP_REPLICATION尤其在自动化脚本还没成熟的阶段。group_replication_member_expel_timeout10把默认的 5 秒拉到 10 秒。这个参数决定多久收不到心跳就认为节点挂了并驱逐。默认 5 秒对虚拟化和容器环境偏激进一次宿主机迁移或者存储抖动就可能误判。拉到 10 到 15 秒代价是真实故障时切主慢几秒换来的是少踩误驱逐的坑。group_replication_autorejoin_tries3表示被驱逐后自动重试加入 3 次每次间隔 5 分钟。默认是 0也就是被踢了就躺平得人工介入。设成 3 能让绝大多数短时网络抖动自愈。binlog_row_imageFULL是 MGR 的硬性要求minimal 会直接拒绝启动。原因是认证器需要完整的行前后镜像来计算写集合。log_replica_updatesON也是必须的从节点要把收到的事务写进自己的 binlog否则它无法把自己的日志继续传给别的节点新节点加入时就找不到完整的恢复源。3.3 账户、克隆与恢复通道的准备工作组复制需要一个复制账户来做增量恢复recovery。8.0.27 之后多了GROUP_REPLICATION_STREAM权限直接把这三条权限给全最省事SET SQL_LOG_BIN0; CREATE USER repl_user10.10.0.% IDENTIFIED BY ChangeMe_2024; GRANT REPLICATION SLAVE ON *.* TO repl_user10.10.0.%; GRANT BACKUP_ADMIN ON *.* TO repl_user10.10.0.%; GRANT GROUP_REPLICATION_STREAM ON *.* TO repl_user10.10.0.%; SET SQL_LOG_BIN1; CHANGE REPLICATION SOURCE TO SOURCE_USERrepl_user, SOURCE_PASSWORDChangeMe_2024 FOR CHANNEL group_replication_recovery;注意SET SQL_LOG_BIN0包住建用户语句。如果这些语句进了 binlog会广播到全组在新节点加入时可能因为用户已存在而报错。这是个很典型的坑我第一次做的时候就在这栽过日志里报Operation CREATE USER failed for repl_user排查了半天才发现是 binlog 里的历史记录在作祟。如果集群数据量已经很大新节点加入时靠 binlog 追平会非常慢。8.0.17 之后内置了克隆插件group_replication_clone_threshold默认 100000意思是如果落后的事务数超过这个阈值就走克隆物理拷贝而不是增量恢复。大集群建议把这个值调小比如 10000让新节点优先走克隆。前提是目标节点上得装克隆插件INSTALL PLUGIN clone SONAME mysql_clone.so;克隆会清空目标节点的数据目录所以千万不要在一个已有数据的节点上随手执行加入操作尤其是那种想试试看的心态。4. 实操三节点集群从零跑起来4.1 初始化实例与首次启动检查三台机器上分别初始化数据目录用mysqld --initialize-insecure生成空实例生产环境当然要换成带临时密码的初始化方式然后立刻改掉。mysqld --defaults-file/etc/my.cnf --initialize-insecure --usermysql mysqld_safe --defaults-file/etc/my.cnf --usermysql 启动后先做几项基础检查这一步别省SELECT server_id, gtid_mode, enforce_gtid_consistency, binlog_format, binlog_row_image, log_replica_updates; SHOW PLUGINS;SHOW PLUGINS里要能看到group_replication状态是 ACTIVE如果显示 OFF 或者压根没有说明plugin_load_add没生效通常是配置文件路径或者权限问题。另外确认gtid_executed是空的如果初始化后就有 GTID 记录说明之前跑过什么务必清干净再入组。还有一件事把每台机器的防火墙规则先打通3306 和 33061 都要双向可达用nc -zv或者telnet从三个方向各测一遍。我坚持从每个节点向其余两个节点都测因为单向放通的策略在某些安全组配置里很常见而 MGR 的心跳是双向的。4.2 引导第一个节点顺序搞反就白干引导只在一个节点上做且只做一次。这里最容易出的错是三台都设了bootstrap_groupON结果起了三个独立的单节点组互相看不见。正确姿势SET GLOBAL group_replication_bootstrap_groupON; START GROUP_REPLICATION; SET GLOBAL group_replication_bootstrap_groupOFF;执行完立刻查状态SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;应该看到一个成员状态 ONLINE角色 PRIMARY。如果卡在RECOVERING很久大概率是恢复通道有问题去错误日志里搜group_replication_recovery。引导成功后把另外两个节点的恢复账户指向当前主节点。因为它们已经执行过上面那段CHANGE REPLICATION SOURCE直接加入即可START GROUP_REPLICATION;第二个节点加入时会经历RECOVERING阶段从主节点拉取缺失的事务。数据量小的话几秒钟数据量大就看克隆或者 binlog 追平的速度。加入成功后replication_group_members里会看到三个成员其中两个 SECONDARY。注意引导命令SET GLOBAL group_replication_bootstrap_groupON执行完后即便START GROUP_REPLICATION失败也要记得把它设回 OFF否则下次重启会意外重新引导产生分叉的组。4.3 验证读写路由与只读约束单主模式下非主节点默认会被设置成super_read_onlyON这是 MGR 自己维护的不要手动去改。我们可以验证一下-- 在从节点执行 SELECT super_read_only; -- 期望 1 INSERT INTO t1 VALUES (1); -- 期望报错应用侧怎么路由两条路。第一条是应用自己感知通过查询SELECT MEMBER_ROLE FROM performance_schema.replication_group_members WHERE MEMBER_IDserver_uuid;来判断本机是主是从主库连接池走写从库连接池走读。第二条是前面挂一个支持 MGR 感知的代理层。我偏好第一条因为它不引入额外组件故障域更小代价是应用需要一点改造。需要强调的是主库切换之后原来的主会变成只读应用的写连接必须能自动重连到新主。如果你的连接池只配了一个主库地址切主那一刻所有写请求会全部报错。这是 MGR 上线后最常见的看起来切成功了但业务全挂的原因。现在的连接池基本都支持多主机配置把三个节点地址都填进去让它按角色发现。5. 故障演练切主、维护与脑裂处理5.1 主库宕机时到底发生了什么我在测试环境做过很多次kill -9主库进程的实验。过程大致是这样主库进程消失心跳停止其余两个节点在member_expel_timeout到期后把故障节点标记为 UNREACHABLE 并驱逐出组组内剩下两个节点满足多数派2/3触发选主选出新主新主切换为可写老主在replication_group_members里的状态变为 ERROR 或消失。整个过程的耗时由几部分构成心跳检测时间 驱逐超时 选主开销。我实测在 10 秒驱逐超时的配置下从 kill 到新主可写大约 12 到 15 秒。这个时间窗口内写入全部失败业务侧必须能容忍或者靠重试兜住。选主规则在 8.0.17 之后可以用group_replication_member_weight影响取值 0 到 100权重高的优先当选。我一般给主库候选节点设成 60备选 50纯读节点设 0。这不是硬性指定而是在同等条件下倾向谁避免选到一个配置较弱的节点上去。如果你需要主动切主比如要升级主库所在宿主机用SELECT group_replication_set_as_primary(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx);传入目标节点的MEMBER_ID。这个命令会等待目标节点追平所有事务再切换是可控的优雅切主比杀进程靠谱得多。5.2 滚动升级与节点维护的正确顺序升级不能三台一起停。标准顺序是先升级所有从节点每升一台就让它重新加入并追平最后再切主、升级原主节点。单台从节点的操作序列STOP GROUP_REPLICATION; -- 关闭实例、替换二进制、启动实例 START GROUP_REPLICATION;加入后确认状态回到 ONLINE再动下一台。全部从节点升完之后执行group_replication_set_as_primary把主切到已升级的节点然后对原主重复同样的操作。这里有个细节滚动升级期间新旧版本的通信协议可能不兼容8.0 提供了group_replication_set_communication_protocol(8.0.30)之类的命令来显式降级协议版本让老节点也能参与。不设置的话新版本节点加入时可能被拒绝报协议不匹配。这个命令是可逆的升级完成后可以再调回最新版本。5.3 网络分区、脑裂与自动重加入真正让人头疼的不是节点宕机而是网络分区。假设三节点集群里 A 和 B、C 之间的网络断了A 变成了少数派。MGR 的设计是少数派节点会主动停止服务进入 ERROR 状态因为它无法确认自己是否还持有最新数据多数派那边继续正常工作选出新主。这个设计避免了脑裂但也意味着网络恢复之后A 需要重新加入。如果group_replication_autorejoin_tries设了 3A 会自动尝试前提是它能连上组内多数派。如果 3 次都失败A 就停在 ERROR 状态需要人工介入STOP GROUP_REPLICATION; START GROUP_REPLICATION;在重新加入之前一定要确认 A 上的数据没有超前于组内。正常情况下不会因为少数派不会接受写入。但如果有人在 A 上手动关掉了super_read_only写了数据那 A 上的事务就和组内分叉了重新加入会被拒绝。这种情况只能用克隆重建或者从备份恢复。所以千万不要在生产环境手动摘掉只读限制这是红线。6. 参数调优与常见故障速查6.1 流控、并行应用与延迟治理MGR 有一个流控机制flow control当某个节点的认证队列或者应用队列积压超过阈值时会反过来压制主节点的写入速度。这机制本意是防止慢节点被甩太远但配置不当会变成一台慢拖垮全组。相关参数参数默认建议说明group_replication_flow_control_modeQUOTAQUOTA保持默认别关group_replication_flow_control_applier_threshold2500010000~20000应用队列积压阈值group_replication_flow_control_certifier_threshold2500010000~20000认证队列积压阈值replica_parallel_workers48~16从节点并行应用线程数把阈值调小会更早触发限流写入更平滑但吞吐上限也低。调大则相反。我的经验是把阈值设成峰值 TPS × 2 秒左右让短时突发能被吸收持续积压才限流。并行应用这一块8.0.23 之后单主模式也支持基于 LOGICAL_CLOCK 的并行复制配置replica_parallel_typeLOGICAL_CLOCK和replica_parallel_workers8能显著降低从节点的应用延迟。之前单主模式只能用 DATABASE 级别并行遇到单库热点表基本没效果这是很多团队觉得 MGR延迟比传统主从还高的真正原因。6.2 加入失败、状态卡住的高频原因我把遇到过的排查整理成表按现象找原因会快很多现象大概率原因处理方式START GROUP_REPLICATION 超时无响应33061 端口不通检查 allowlist 与防火墙卡在 RECOVERING 不动恢复账户权限不足或密码错误检查 repl_user 的三项权限报错 This member has more executed transactions节点本地有额外事务用克隆重建该节点报错 binlog_row_image 不符配置遗漏改为 FULL 并重启加入后立刻被驱逐握手阶段网络抖动调大 expel_timeout检查 MTU全组停止服务剩余节点不足多数派恢复至少一个故障节点MTU 这个问题值得单独说一句。如果机器之间走了不同的网络路径MTU 不一致会导致大包被丢弃表现为小事务正常批量写入就断连。用ping -s 1400 -M do从各方向测一下能快速定位。6.3 监控该盯哪几个指标MGR 的监控不必上很重的系统performance_schema里几张表就够用了。我固定采集这几个-- 成员状态 SELECT MEMBER_ID, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members; -- 事务积压与冲突 SELECT MEMBER_ID, COUNT_TRANSACTIONS_IN_QUEUE, COUNT_TRANSACTIONS_CHECKED, COUNT_CONFLICTS_DETECTED, TRANSACTIONS_APPLIED FROM performance_schema.replication_group_member_stats; -- 当前主节点 SELECT MEMBER_HOST FROM performance_schema.replication_group_members WHERE MEMBER_ROLEPRIMARY AND MEMBER_STATEONLINE;COUNT_TRANSACTIONS_IN_QUEUE持续大于 1000 就要警惕了说明应用跟不上。COUNT_CONFLICTS_DETECTED在单主模式下应该长期为 0一旦有增长说明有节点在偷偷写入这是必须立刻查的异常。MEMBER_STATE不为 ONLINE 就要告警尤其是从 ONLINE 变成 UNREACHABLE 再变成 ERROR 的路径。最后一个实操心得把主节点身份做成一个 5 秒刷新一次的指标接进应用的配置中心。切主之后应用能在几秒内感知到新主地址比等连接池被动报错要体面得多。这套机制我用了两年切主期间业务侧的失败请求从持续十几秒全失败降到零星几个重试成功体感差别非常大。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑