资讯动态

MySQL Group Replication(MGR)高可用集群搭建与排坑实践

发布时间:2026/10/21 23:33:10 来源:尧图企业网站定制
MySQL三台机器做高可用听起来是个很成熟的方案但真正动手之前我心里一直犯嘀咕主从复制关键时刻丢数据半同步又容易卡主库MHA那套切换脚本越维护越心虚。后来我把MySQL Group ReplicationMGR从零搭了一遍才理解为什么说它是高可用数据库的“组队”玩法。这篇文章就把我整个搭建过程和踩过的坑完整记录下来从组复制原理、参数设置、节点加入到故障切换实测适合正在评估MGR或者准备在生产环境落地的DBA和运维同学参考。1. 为什么我觉得MGR才是数据库高可用的“正确打开方式”1.1 主从复制那些让人难受的瞬间先聊点背景。很多团队的第一套高可用方案都是经典主从复制加一个脚本做VIP漂移主库挂了之后脚本检测不到心跳就把VIP切到从库上。这套方案问题不在于“能不能切”而在于切换之后数据到底丢了多少。异步复制下主库提交事务之后binlog才往从库传主库瞬间宕机最后几十个事务可能根本没来得及发给从库强行切换就等于承认丢数据。半同步虽然能保证至少一个从库收到binlog但这里有另一个坑半同步在主库提交等待期间如果从库ack丢失会退化成异步模式很多团队没监控这个退化状态等于裸奔。我遇到过最难受的一次是某个凌晨从库延迟飙到几十分钟主库刚好断电当时除了等主库恢复没有任何选择。所以后来我研究MGR最核心的一个原因是它从机制上解决了解释不清的“数据一致性”问题。1.2 MGR和它们到底差在哪MGR本质上是一种组复制方案和传统主从复制的最大区别是多个节点之间形成一个复制组所有成员都能看到彼此状态事务必须在组内大多数节点确认后才真正提交。这意味着没有“切换后丢数据”的尴尬。因为每个已提交事务都已经存在于组内多数节点上。选主是自动的。主节点没了组内会通过共识机制从存活成员里选出新主不需要你写脚本去猜谁的数据最新。节点角色不是固定死的。无论单主还是多主节点身份在运行中是动态维护的。我用一个比喻来理解MGR传统主从像“老板说了算”老板挂了大家得开会选新老板MGR更像“群聊投票”任何重要操作都要绝大多数人先看到、同意了才算数。所以标题里写“组队”一点都不夸张。2. MGR的“组队”原理先搞懂组在怎么玩2.1 组通信每个成员都在“群聊”MGR的底层是一个叫Group Communication System的模块它保证组内每个成员收到的消息顺序是一致的。所有数据变更都会通过这个模块广播出去节点之间互相确认达成一致后事务才会在各自本地提交。这里需要用到Paxos的一致性思路MGR实现里用的是XCom简单理解就是三个成员A、B、C一个事务到了AA会把这件事广播给B和C必须等多数派确认A才在本地提交。B和C也会在本地重放同一个事务。这意味着不用你手动指定谁是主组内成员自己协商。只要存活节点过半组就能继续工作存活节点不过半组自动只读或停止服务防止两头都在写。刚开始我也觉得Paxos很玄但你实际搭建的时候只需要关心配置参数是否正确。理解这几个概念就够用了组视图谁在组里、成员状态ONLINE/OFFLINE/RECOVERING、事务认证这个事务能不能在组内成功执行。2.2 单主模式和多主模式怎么选MGR提供两种模式创建组的时候可以通过参数指定我强烈建议新手直接选单主模式。对比项单主模式多主模式可写入节点只有主节点所有节点均可写事务冲突风险低高并发写入能力受主节点限制理论上更高实际需要拆key运维复杂度低高推荐场景大多数业务明确知道自己在做什么的团队多主模式下两个不同节点的“写”可能同时对同一条记录更新MGR会通过事务依赖检测让其中一方失败。这个冲突检测靠的是写集中的主键信息所以你有触发器或者外键的时候多主的冲突检测会受限并不建议。我在自己环境里测过多主压力上来之后冲突重试率明显上升对应用层影响很大。2.3 成员管理与故障检测MGR还有一个我觉得非常省心的能力成员状态是实时在performance_schema里可见的。故障检测机制会周期性地给所有成员发心跳如果某个成员超过阈值没响应就会被认为疑似故障然后触发重新配置组视图。这里有一个参数值得记住group_replication_member_expel_timeout。默认值很短如果只是网络抖动一下节点还没真死就可能被移出组。生产环境我一般会调高一些给短暂网络波动留缓冲。别小看这个参数后面有一节我会专门讲它帮我躲过一劫。3. 搭建前的环境准备这些配置没做对后面全白搭3.1 版本选择和网络规划我的建议是统一使用MySQL 8.0.17以上版本因为8.0.17之后官方加入了clone插件新节点加入组时可以跳过传统备份导入的漫长等待直接通过clone把数据拉齐这个体验和老版本比完全是两个世界。网络层面MGR所有节点之间需要能互相访问通常需要两个端口一个是MySQL服务端口3306另一个是组通信专用的端口我习惯用33061。这个33061不需要暴露公网但防火墙里必须放行。很多新手只放行了3306结果启动组复制的时候一直卡在RECOVERING查日志才知道是组通信端口不通。我搭的环境是三台独立的虚机都是内网IP之间延迟保持在1ms以内。MGR对延迟非常敏感如果你要跨地域组集群我不建议直接用MGR考虑其他方案更稳。3.2 那些必须改的MySQL参数MGR对MySQL实例的基础设置有一堆强制要求我直接列一份我在所有节点上都会用的最小配置[mysqld] server_id 1 # 每个节点必须唯一 gtid_mode ON enforce_gtid_consistency ON binlog_checksum NONE log_bin ON log_slave_updates ON transaction_write_set_extraction XXHASH64 loose-group_replication_group_name aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee loose-group_replication_start_on_boot OFF loose-group_replication_local_address 192.168.1.11:33061 loose-group_replication_group_seeds 192.168.1.11:33061,192.168.1.12:33061,192.168.1.13:33061 loose-group_replication_single_primary_mode ON loose-group_replication_enforce_update_everywhere_checks OFF逐个解释一下gtid_mode和enforce_gtid_consistencyMGR要求每个事务必须有全局唯一标识方便组内各节点对比日志这两个必须开。binlog_checksumNONE组复制协议对checksum有要求官方文档明确建议关闭不然可能导致成员通信校验失败。log_slave_updates从节点也要记录完整的binlog这样任何节点都能在故障时被追认成新主都有完整日志。transaction_write_set_extraction用来做事务冲突检测的hash算法MySQL 8.0里填XXHASH64即可。loose-前缀表示这个参数是插件相关的MySQL在插件没加载时会忽略它方便你先启动实例再加载插件。3.3 两个容易忽略的存储要求第一磁盘空间。MGR每个节点都要保存完整数据、binlog和relay log意味着三节点集群总数据量至少是单节点数据量的两三倍。我在压测时发现大事务会同时产生所有节点的relay log如果磁盘预留不够可能节点直接被写满然后触发成员退出。第二网络带宽。每个写事务都要通过组通信广播给所有成员带宽占用比单主复制高很多。你做一个全表UPDATE大事务的时候组通信消息会非常大这既是带宽压力也是CPU压力。4. 一步步把第一个节点拉进“组”4.1 初始化实例和创建复制用户配置好参数文件后先正常启动MySQL实例。我习惯第一个节点叫node1先把三个实例分别初始化好root密码也设好。接着登录node1创建用于组复制恢复的复制账号CREATE USER repl% IDENTIFIED BY your_password; GRANT REPLICATION SLAVE, BACKUP_ADMIN ON *.* TO repl%;这里BACKUP_ADMIN权限是为了让新节点能够使用clone插件从捐赠者拉取数据。如果你只想用传统方式mysqldump/xtrabackup同步不加这个权限也行但我强烈建议你能用clone就别折腾。然后加载组复制插件INSTALL PLUGIN group_replication SONAME group_replication.so;执行后可以确认插件状态SHOW PLUGINS;看到ACTIVE状态的group_replication插件就算加载成功。4.2 启动组复制的关键步骤MGR有一个很特别的地方第一个节点启动组的时候需要手动告诉它“我是种子我来建组”。这个逻辑是通过group_replication_bootstrap_group参数控制的。在node1上执行SET GLOBAL group_replication_bootstrap_group ON; START GROUP_REPLICATION; SET GLOBAL group_replication_bootstrap_group OFF;第二步START GROUP_REPLICATION非常重要它是异步命令执行成功只代表发起请求。真正是否加组成功需要立即查询成员状态SELECT * FROM performance_schema.replication_group_members;正常情况下应该看到一行记录MEMBER_STATE是ONLINEMEMBER_ROLE是PRIMARY。如果你看到的是RECOVERING别慌说明节点正在尝试从捐赠者拿数据。先查复制通道日志SHOW REPLICA STATUS FOR CHANNEL group_replication_recovery\G重点看Last_IO_Errno和Last_SQL_Errno大多数问题都藏在这里。4.3 怎么确认种子节点真的动了这一步做完很多人就直接去加第二个节点结果卡住。我习惯先做两个额外验证确认组名和本地地址都生效了SELECT * FROM performance_schema.replication_group_member_stats\G里面能看到VIEW_ID这个值在组重新配置的时候会变化如果所有成员显示同一个VIEW_ID说明组视图是一致的。在node1上写一条测试数据再确认GTID是否正常CREATE DATABASE testdb; USE testdb; CREATE TABLE t1 (id INT PRIMARY KEY); INSERT INTO t1 VALUES (1);哪怕只有一台节点这一写也能确认binlog、GTID和组复制日志链路是通的后面加节点的时候才有底。5. 添加新节点从“单打独斗”到“组队成功”5.1 新节点的准备工作和数据同步node2和node3的配置文件基本和node1一样只需要改server_id和group_replication_local_address里的IP。注意一点node2和node3不能手动执行SET GLOBAL group_replication_bootstrap_groupON这个操作只能做一次且只能在第一个节点上做。然后登录node2加载插件INSTALL PLUGIN group_replication SONAME group_replication.so;接着配置recovery通道。MGR里新节点加入组时会通过一个专门的复制通道group_replication_recovery从组内现有成员通常是主节点拉取数据这个通道需要知道用什么账号连接CHANGE MASTER TO MASTER_USERrepl, MASTER_PASSWORDyour_password FOR CHANNEL group_replication_recovery;然后启动START GROUP_REPLICATION;如果一切正常node2会先进入RECOVERING状态clone插件自动从node1拉取快照完成后变成ONLINE。5.2 复制用户与恢复通道的细节这里有个容易踩的坑复制用户我建议大家在所有节点上都建一样的包括node1自己也要建。因为MGR是去中心化的任何一个节点以后都可能成为捐赠者如果只有node1有repl用户将来node2想给node3传输数据就会因为node2上找不到这个用户而失败。我最初在node2上只配了recovery通道没建用户加node3的时候来回报权限错误排查了很久才意识到问题。所以我的经验是把建用户、授权、配通道做成一个固定脚本每台机器执行一遍省心很多。另一个细节如果数据量比较大clone默认超时时间可能不够我习惯加一行loose-clone_donor_timeout 120或者执行SET GLOBAL clone_donor_timeout 120;在数据量超过100GB的场景下这个很有用。5.3 验证组里每个成员的状态等所有节点都加入后在任意节点执行SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;理想输出是三行都是ONLINE其中一个是PRIMARY另外两个是SECONDARY。这时候还可以做一次完整验证在node1插入数据分别在node2和node3查确认数据实时可见。我实际测过从node1插入到node2可见基本在几十毫秒内前提是网络正常且没有大事物拖慢relay log应用。这个延迟和时间如果持续拉大就要检查磁盘IO了。6. 故障转移实测节点挂了以后到底发生什么6.1 模拟主节点宕机的全过程搭建完成不代表真的高可用必须实际演练过故障转移才算数。我当时的做法是直接在node1上执行SHUTDOWN模拟主节点突然断电。停机后隔几秒查node2上的成员状态SELECT MEMBER_ID, MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;能看到node1的状态变成OFFLINE同时node2和node3继续存活。关键看下一步组内什么时候选出新主。MGR的故障检测加上选主通常需要几秒时间。因为我设了group_replication_member_expel_timeout5所以默认要等待检测窗口加上投票实际大概在5到10秒之间完成新主选举。node2会被选为新PRIMARYnode3自动反向跟随node2。6.2 从库怎么接管写入MGR自动选主之后应用不会自动感知新主IP。生产环境里我一般会用MySQL Router或者应用连接层自己做读写分离只把写流量指向当前主节点。MySQL Router可以通过执行mysqlrouter --bootstrap admin192.168.1.12:3306 --directory router_home配置完成后写连接走Router它会根据组内动态路由到主节点。故障切换时Router会自动感知主节点变化应用几乎无感。如果你不想引入Router也可以做一个简单的健康检查脚本定期查询replication_group_members把当前PRIMARY的IP地址写到一个统一DNS或者VIP配置里。但这种方式比Router多了一层手动操作我不太推荐。6.3 重新加入原主节点要注意什么故障演练完了要把node1重新加回组。这里有个很容易犯的错误直接START GROUP_REPLICATION然后卡在RECOVERING。正确做法是先把node1正常启动确认实例基础状态OK再执行START GROUP_REPLICATION;node1会自动从当前主节点node2通过clone或者增量日志恢复数据恢复到最新状态后转为SECONDARY ONLINE。我踩过一次比较深的坑是node1重新加入后因为当时它已经旧数据落后太多且clone没被触发recovery通道一直报错。最后解决方案是手动告诉它重新指定捐赠者SET GLOBAL clone_valid_donor_list 192.168.1.12:3306; START GROUP_REPLICATION;这里其实也反映出MySQL 8.0.17之后clone自动选捐赠者的逻辑多数时候靠谱但偶尔需要手动干预懂这个原理比背命令更重要。7. 这些坑我用生产事故换来的经验7.1 网络延迟导致反复退出组MGR最典型的故障就是组内成员因为网络抖动被误判然后反复进入退出状态。第一次遇到的时候业务反馈写操作偶尔超时我登上每个节点看状态发现总是有一个节点在RECOVERING和ONLINE之间反复横跳。查MySQL错误日志关键报错是类似于Member was expelled due to member lease expiration。原因就是节点之间的心跳消息因为短暂阻塞没及时送达触发了成员租约过期机制。解决方案是调大两个参数SET GLOBAL group_replication_member_expel_timeout 30; SET GLOBAL group_replication_communication_stack MYSQL;第一个参数给了误判缓冲第二个参数是把组通信底层改成MySQL自带协议如果你用的是XCom且网络栈不稳也可以试试改成MYSQL通信栈。我的实际经验是网络抖动和延迟高是两个不同问题延迟高可以通过调大超时缓解但网络丢包导致的反复重启可能还需要检查交换机、网卡参数。7.2 大事务引发的分布式死锁MGR对超大事务是非常不友好的。某次我在测试环境下模拟一个全表UPDATE更新了几百万行结果直接导致整个组卡住其他成员的relay log应用进度全部停滞最后还触发了一个节点被移出组。原因是MGR的事务认证阶段需要校验写集超大事务的写集会非常巨大组内每个节点都要做同样的校验运算CPU和内存瞬间被打满。而且这种大事务在提交前处于锁定状态所有其他写操作都被阻塞整个组看起来就像“死”了。后来我在生产规范里加了两个约束单事务影响行数超过一定量级必须拆批比如每次UPDATE/ DELETE控制在1万行以内。禁止在业务高峰期执行全表更新确有需要就通过工具分批次跑。如果你确实有很多大事务场景建议优先考虑单节点写入模式并把binlog_row_value_options和行格式调整到位但这只能缓解不能根治。7.3 版本升级和混合版本踩坑MGR插件的版本兼容是极其挑剔的。我在升级到一个小版本之后发现组内一个节点始终无法加入信息里提示版本不一致。原因是旧节点的组通信协议版本较旧和新节点的协议版本不兼容。传统主从复制对版本差异容忍度很高但MGR要求所有成员在协议层面必须匹配。解决思路有两种一是直接全组滚动升级把每个节点都升到同一个新版本然后重启组二是先升级大多数节点后把旧版本节点强制剔除再让它用新版本重新加入。我建议你把版本统一作为MGR组管理的硬性准入标准每次升级在新的测试组里先验证一遍再动生产。最后再分享一个小技巧如果你准备在生产环境正式上MGR但心里没底我的一个实用建议是建一个“影子组”不接任何业务流量完全模拟你们实际的写入模式先跑两周。数据一致性、故障切换、版本兼容全在这个影子组里演练一遍再切生产。我在测试阶段积累的脚本和踩坑经验最后几乎都原样用在了生产部署上。MGR不是说搭完三个节点就能高枕无忧组件的价值在于它把“主从切换”从靠运气变成了靠共识但真正让它稳定的还是你对这套机制每个细节的理解和反复实操。

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

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

免费获取报价 →
↑