news 2026/10/3 9:47:01

MySQL高可用方案MMM深度解析:主主复制与VIP漂移实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL高可用方案MMM深度解析:主主复制与VIP漂移实战

做MySQL高可用这些年,接触过的方案不少,但有一个老家伙让我印象特别深——MMM,全称Multi-Master Replication Manager,中文常翻成多主复制管理器。不少人一听到这个名字就当作“老旧古董”忽略掉,可真在存量生产环境里蹲过故障的人都知道,在MySQL主主复制架构下,它那套监控加VIP漂移的设计,关键时刻是真能顶上去的。这篇文章我结合自己实际部署和维护MMM集群的经验,把它的技术原理讲透,再把从零搭建到故障切换的完整流程写给你。文章不只讲概念,还会把配置文件怎么填、脚本怎么调、切换时哪些地方最容易翻车这类实操细节一并说清楚。如果你正在评估MySQL高可用方案,或者要接手一套老的MMM集群,这篇应该能帮你省下不少试错时间。

1. MMM到底是什么:主主复制为什么需要它

1.1 从MySQL原生复制的痛点说起

先还原一个很常见的场景。一套业务系统,数据库是双节点,用的是MySQL原生主从复制:A库是主库,提供写入和读取,B库是备库,平时只做异步复制,偶尔挂个只读查询分担压力。流量不小,A库负载已经挺高,团队知道单点风险,但一直觉得“B库在呢,挂了也能切”,所以没动过。结果有一天A库磁盘写满,MySQL进程直接hang住,业务开始报错。半夜值班的人被叫起来,手动改VIP、改应用连接配置、把B库提升为主库,整个过程四十多分钟,业务就停摆了四十多分钟。

这个例子一点都不夸张,MySQL原生主从复制本身只解决“数据有备份”的问题,它不解决“主库故障后业务怎么快速恢复”的问题。在主从架构里,主库是单点,谁做主、谁做备、VIP挂在哪、复制中断了要不要重建,这些全靠人肉维护。一旦故障发生在凌晨,或者操作的人对架构不熟,恢复时间只会更长。

所以高可用需要的不只是复制,而是复制之上的一整套调度逻辑:监控节点状态、判断故障类型、自动切换主备、重新分配业务入口、让从库重新对齐复制关系。这些事MMM全包了。

1.2 MMM的设计定位和组件构成

MMM是2005年开始的项目,作者是Tony Darnell,后来交给加拿大公司继续维护。它不修改MySQL源码,而是在MySQL复制能力之上,做了一层监控与调度框架,通过虚拟IP的漂移来实现高可用。它的核心思路可以用一句话概括:用独立监控节点当“大脑”,用各DB节点上的agent当“手脚”,再配合MySQL自身的复制机制完成任务。

标准架构里角色分得很清晰,这也是理解MMM的第一步:

  • writer master:当前提供写入的主库,持有writer VIP;
  • standby master:作为writer的备份节点,与writer保持双向复制,故障时顶上去;
  • slave节点:只读从库,从writer同步数据,持有reader VIP供只读业务连接;
  • monitor节点:独立部署,运行mmm_mond进程和mmm_control命令,负责整个集群的决策。

在双主加多从的普通形态里,两个主节点组成主主复制,同时互为对方的从库。业务写入全部集中在writer master上,standby master平时不承接写入,只通过复制保持数据同步。一旦writer故障,monitor会迅速判死,把writer VIP漂移到standby上,并通知所有从库重新指向新的写入点。

这种设计最打动我的一点是透明。所有逻辑就是一组Perl脚本加配置文件,出了问题可以逐行读脚本、翻日志,比遇到黑盒中间件无从下手强得多。在那个服务器资源紧张、MySQL版本保守、中间件选择少的年代,MMM用朴素的方式解决了大问题,这也是它能在生产环境存活这么多年的原因。

2. 核心机制拆解:监控探测与VIP漂移是怎么运转的

2.1 双心跳检测:monitor如何判断节点故障

MMM的monitor节点运行着mmm_mond进程,这是集群唯一的决策中心。它通过两类心跳来感知每个DB节点的状态:

  • ping探活:周期性发送网络探测包,判断主机网络层是否可达;
  • MySQL连接检查:通过TCP连接到MySQL实例执行轻量查询,判断MySQL服务是否真的能对外提供服务。

我之所以强调“两类心跳缺一不可”,是因为它们语义完全不同。ping通只能说明主机内核和网卡活着,MySQL进程可能早就挂了,写请求照样失败;反过来,MySQL连接超时也可能只是主机负载过高或者连接数打满,不代表机器整体已经宕机。MMM把这两种检查结合起来,本质上是做了故障的初步分类,避免因为单方面误判而频繁切换,把业务抖死。

有一点需要特别注意:agent是不做决策的。各DB节点上的mmm_agentd进程只负责被动执行monitor下发的指令,比如绑定VIP、移除VIP、启动或停止复制线程、切换read_only状态。这种中心化决策加分布式执行的模式,优点是逻辑集中好维护,缺点也很明显——monitor本身成了新的单点。所以生产环境我强烈建议monitor用独立机器,资源给足,网络单独划一个管理网段,别让业务流量和监控流量挤在一起互相干扰。

2.2 VIP漂移机制:writer VIP和reader VIP的区别

MMM的虚拟IP管理是它最值钱的能力。一个集群里通常会有两类VIP:

  • writer VIP:整个集群同一个时刻只能有一个节点持有,它指向当前提供写入的主库;
  • reader VIP:可以同时挂在多个从库上,供只读业务、报表查询、分析任务连接。

monitor在认定writer故障后,会触发一组切换动作集合,大致是:

  1. 尝试从故障节点上移除writer VIP;
  2. 把writer VIP绑定到standby master节点;
  3. 在备用节点上关闭read_only,让应用可以写入;
  4. 通知各个从库重新执行复制指向操作,把数据源切到新的writer。

流程看着简单,但容易出问题的细节非常多。VIP的移除和绑定是通过arp命令和网络配置实现的,命令执行的时机、失败重试机制都直接影响切换是否成功。最典型的分险是原主库并没有完全宕机,只是网络分区导致monitor联系不上它,这时如果另一边的备用库被提升,两边可能同时对外提供写入服务,也就是常说的split-brain脑裂。MMM自身没有特别完善的防脑裂机制,通常要靠交换机隔离、防火墙规则或者业务层的幂等设计来兜底,这点在规划架构时就要想明白,别指望切换脚本能处理所有玄学问题。

另外,也别把MMM想象成零停机方案。切换期间业务写请求会有数秒到十几秒的中断,这在高可用方案里已经属于中规中矩的水平,不能和单机热备或者共享存储那种秒级切换比。

2.3 复制链维护与数据一致性的边界

MMM不光管VIP,还管复制链。双主加多从的架构里,复制是这样组织的:主库A和主库B互设对方为主,形成环形复制;从库C、D各自从当前的writer master拉取binlog。切换发生时,MMM要让standby变成新writer,再指挥所有从库执行change master重新指定源头。

必须提防的是循环复制。MySQL通过server-id来标识binlog来源,每次事件都会带上产生它的server-id,从库收到事件后不会再次执行自己server-id发出的事件,这就是MySQL天然防止回环的机制。所以在双主配置里,每台节点的server-id必须全局唯一,这是一个极容易忽略但极重要的细节。MMM维护脚本里也有一个“禁止复制的库列表”,比如系统库mysql等,避免系统表数据在环形链路中反复传播导致意外变更。

关于数据一致性,我还是要泼一点冷水。MMM不是一个数据同步工具,它依赖的是MySQL自身的异步复制。如果writer在宕机前有一部分binlog还没传到备用节点,切换后这部分数据就是丢了。MMM不提供数据补偿,只负责把VIP和复制关系切过去。所以这类方案适合消息、日志、内容数据这类对丢失容忍度相对高的业务,如果是订单、账户余额这种核心账务数据,建议不要裸奔。

3. 从零搭建MMM集群:环境规划与完整配置步骤

3.1 最小可用部署架构与IP规划

这里我给一套我实际用过的两主两从拓扑,你可以直接照搬,再按自己环境改IP。

角色分配:

  • node1、node2:主主互备节点,node1初始是writer,node2是standby;
  • node3、node4:从库节点,提供只读能力;
  • monitor:独立监控机,只跑监控服务。
节点IP承载VIP角色说明
node110.0.0.1010.0.0.100 writer初始写主库
node210.0.0.1110.0.0.101 readerstandby备主
node310.0.0.1210.0.0.102 reader只读从库
node410.0.0.1310.0.0.103 reader只读从库
monitor10.0.0.14无监控节点

关于资源,MySQL版本我这里建议5.7或8.0都可以跑,操作系统CentOS 7/8、Ubuntu LTS都行。但如果你用MySQL 8.0,注意老版本MMM的Perl脚本默认使用的认证插件可能存在兼容问题,需要把MySQL用户的认证方式调整成mysql_native_password,或者找社区补丁适配caching_sha2_password。安装MySQL的过程这里就不重复了,网上教程很多,但一定要记住把二进制日志和复制相关参数在初始配置阶段就打开,不然后面改起来麻烦。

3.2 MySQL端配置:双主互备的参数细节

双主节点的MySQL配置是整套架构的基石。以node1为例,my.cnf中关键的配置项长这样:

[mysqld] server-id = 1 log-bin = mysql-bin binlog_format = ROW relay-log = relay-bin log_slave_updates = 1 auto_increment_offset = 1 auto_increment_increment = 2

node2的配置除了server-id=2,auto_increment_offset=2之外,其余基本一致。这两个自增参数是双主配置里最容易犯错的点:offset决定了各自主键从哪个起点开始生成,increment限制了同一批连续ID分配给不同节点的间隔。设成offset=1和offset=2,increment=2,就能保证node1生成奇数,node2生成偶数,即使两边同时写入也不会出现主键冲突。

还要强调几个容易踩的配置点:

  • log_slave_updates=1必须开。它的作用是把从库通过中继日志回放的事件,也写进这台机器的binlog。如果不开,备用节点作为从库收下的binlog不会产生自己的binlog,后续继续向后传递复制链时就会断掉;
  • binlog_format用ROW更稳妥。在主主复制和双节点互备的场景里,ROW格式对数据一致性最好,也能避免某些SQL函数在两端执行结果不一致的问题;
  • read_only参数不要手工固定写在配置里,因为MMM会通过agent脚本动态切换read_only状态,如果配置文件里写死,切换时就会打架。

复制账号这一步不能省。在两个主节点和所有从库上都要创建同一个复制账号:

CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@strong123'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%'; FLUSH PRIVILEGES;

然后在node1和node2上互相设置复制源。MySQL 8.0较新版本已经用CHANGE REPLICATION SOURCE TO替代了CHANGE MASTER TO,老版本语法也兼容。初始执行时还要注意用SHOW MASTER STATUS确认binlog位置,首次同步建议先做一次全量备份恢复再启动复制线程,避免两边数据起点不一致。

3.3 MMM组件安装与配置详解

MMM通常使用2.2.1版本,源码包编译安装,依赖Perl及相关模块。在CentOS环境下的步骤大致是:

yum install -y perl perl-CPAN perl-DBI perl-DBD-MySQL perl-Net-Ping perl-Algorithm-Diff tar zxvf mysql-mmm-2.2.1.tar.gz cd mysql-mmm-2.2.1 make && make install

安装完会得到几个关键命令:mmm_mond、mmm_agentd、mmm_control,分别对应监控守护进程、Agent守护进程和管理命令。配置文件统一放在/etc/mysql-mmm/下,核心就三个:

  • mmm_common.conf:集群公共定义,所有节点都会读到;
  • mmm_mon.conf:monitor端私有配置;
  • mmm_agent.conf:各agent节点自己的配置。

一份典型的两主两从mmm_common.conf大概长这样:

cluster_name = mmmsite find_mode = strict active_master_role writer <host default> agent_port 9989 replication_user repl replication_password Repl@strong123 ssh_user mmm </host> <host node1> ip 10.0.0.10 mode master peer node2 </host> <host node2> ip 10.0.0.11 mode master peer node1 </host> <host node3> ip 10.0.0.12 mode slave </host> <host node4> ip 10.0.0.13 mode slave </host> <role writer> hosts node1, node2 ip 10.0.0.100 mode exclusive </role> <role reader> hosts node2, node3, node4 ip 10.0.0.101, 10.0.0.102, 10.0.0.103 mode balanced </role>

这段配置有几个关键点需要解释。mode master的两个节点通过peer指定互为对端,MMM据此判断它们之间应该建立双向复制关系,这是“主主”的核心。writer角色的mode用exclusive,保证writer VIP同一时刻只能在一个节点上,否则两个主库同时对外写,数据立刻就会乱。reader角色的mode用balanced,可以在多个从库之间分配reader VIP,注意这里的“负载均衡”是静态的VIP分配,不是依据连接数动态调整,真实并发很高的场景还是要配合业务或代理层来分流。

mmm_agent.conf在每台DB节点上内容都很短,先把node1作为示例:

include mmm_common.conf this node1

每一台DB节点都要把this改成自己的主机名或标识。这里特别提醒,每台agent节点必须和monitor建立SSH互信,因为监控指令有一部分是通过SSH通道下发的,配置互信时建议用一个专用账号,不要直接给root,权限控制和安全合规都要顾及到。

3.4 启动顺序、验证命令与手动切换

我之前第一次部署时不分先后,一股脑把agent和monitor都启动,结果mmm_control show看到一片UNKNOWN。后来总结出正确顺序:

  1. 先在所有DB节点上启动agent服务:
service mysql-mmm-agent start
  1. 再启动monitor节点的监控服务:
service mysql-mmm-monitor start
  1. 最后用mmm_control查看整体状态:
mmm_control show

状态输出正常时大概是这样的:

node1(10.0.0.10) master/ONLINE. Roles: writer(10.0.0.100) node2(10.0.0.11) master/ONLINE. Roles: reader(10.0.0.101) node3(10.0.0.12) slave/ONLINE. Roles: reader(10.0.0.102) node4(10.0.0.13) slave/ONLINE. Roles: reader(10.0.0.103)

确认VIP分配符合预期之后,我通常还会做一次手动切换演练。比如计划内维护需要把写入挪到node2,可以执行:

mmm_control move_role writer node2

这条命令会把writer VIP从node1转移到node2,并触发对应的复制重定向,比手工改应用配置高效太多。手动切换是验证整个集群健康度的最直接手段,我建议每半年至少强制做一次。

4. 故障切换的真实场景与排查经验

4.1 主库宕机后完整切换过程复盘

说一次真实经历。某次下午,node1所在物理机被云平台强制关机,监控第一时间出现告警,mmm_control show里node1从ONLINE变成了OFFLINE。紧接着十几秒后,我看到writer角色已经漂移到node2,reader VIP也重新分配完毕。整个过程没有人干预,业务写入短暂中断后自动恢复。

把这次切换拆开来看,monitor做了这几件事:

  1. 连续若干次探测失败后,把node1判死;
  2. 调用node1上的agent脚本尝试移除VIP,但由于主机已经关机,这一步是失败的,不过不影响后续;
  3. 在node2上执行绑定writer VIP并关闭read_only;
  4. 让node3、node4重新执行change master,把数据源切换到node2;
  5. 更新mmm_control记录,把node2标记为writer。

这里就暴露了一个关键问题:如果node1不是彻底宕机,而只是网络隔离,那么agent脚本可能依然无法执行移除VIP,node1网卡上的VIP就还挂着。业务流量按照VIP的ARP关系仍然会被引到node1,导致切换看起来做了,但实际业务还是连不上。这个场景非常隐蔽,需要提前在网络层或交换机层配置防隔离手段,比如配合防火墙脚本清理ARP表,或者在云平台安全组里隔离故障节点。MMM的Perl脚本本身对这类脑裂场景的防御很弱,必须靠外围保障补齐。

4.2 旧主恢复后的回切流程与数据补偿

旧主修复后重新加回集群,不会自动抢回writer角色。MMM默认会让它作为standby节点上线,但复制关系必须人工处理。我的习惯是不直接复用旧主的binlog继续复制,原因在于旧主宕机期间可能有日志空洞,直接change master可能让数据链出现断层。正确做法是在新主node2上做全量备份,恢复到旧主node1,再重新建立复制关系。

步骤归纳如下:

  1. 在新主node2上执行全量备份,恢复到旧主node1;
  2. 在node1上执行CHANGE MASTER指向node2,启动复制线程;
  3. 用mmm_control show确认node1已被识别为standby,角色为reader之一;
  4. 如果业务需要回切到原主,选择一个低峰窗口执行mmm_control move_role writer node1。

回切不是必须做的操作,但长期不切会让主备角色跟业务预期不一致,以后维护容易懵。每次回切都有几分钟写中断,要提前通知业务方,挑选流量最小的时候做。

4.3 常见故障速查表与排查技巧

故障现象可能原因排查与解决
节点在mmm_control show里长期显示UNKNOWNagent未启动或9989端口不通检查agent进程和防火墙,确认端口监听正常后重启agent
切换后writer VIP还在旧库网络隔离导致agent无法执行脚本清理旧库网卡上的VIP,配合外部ARP清理或防火墙隔离
从库复制中断,Seconds_Behind_Master持续增长新主变更后从库未重指复制源在新主上执行CHANGE MASTER重新指定并启动复制
双主自增ID冲突auto_increment_offset配置不对核对两库offset,确保彼此错开
monitor频繁误判节点宕机网络抖动或探测间隔过短调大check_interval,增加重试次数,monitor用独立管理网段
复制链路报错停住,SQL线程异常binlog格式不一致或ROW事件有冲突用pt-table-checksum检查差异,再用pt-table-sync修复后重启复制
mmm_control命令提示权限不够SSH互信配置不正确重建SSH互信,用专用账号并加入sudo白名单

踩过这些坑之后我最大的体会是,监控节点的稳定性一定要放在第一位。因为monitor一旦乱报,整个集群会跟着频繁误切换,比不切换更伤业务。另外,MMM的日志默认输出在/var/log/mysql-mmm/目录下,监控日志和agent日志要分开看,排查问题时先去看monitor日志对应时间点的决策记录,再去看agent日志看执行结果,顺序对了效率会高很多。

5. 选型边界与替代方案:MMM还能不能用在生产环境

5.1 MMM的局限在哪里

每次有人问“新项目还能不能用MMM”,我的回答都很谨慎。这个项目近年来基本停止活跃维护,遇到MySQL新版本或者操作系统升级带来的兼容问题,可能只能靠社区补丁甚至自己改脚本解决,这个成本要算进去。功能上,它只管理VIP和复制关系,不做分库分表,不支持细粒度的SQL路由,读写分离也偏静态。更麻烦的是数据一致性,它建立在异步复制之上,严格有损,核心账务类业务用它会很心虚。

还有运维上的问题,MMM对网络隔离和脑裂场景的防御很弱,中心化的monitor节点万一自身故障,整个监控链路就没了眼睛。切换过程也不是秒级,几十秒的中断对于很多互联网业务来说已经不可接受了。

5.2 新老方案对比与选型建议

看一个方案能不能用,不能脱离业务场景和团队情况。我把我的思考路径写一下:

  • 如果只是两三个数据库节点做高可用,预算有限,团队对MySQL复制足够熟悉,那MMM完全够用,特别是在存量环境已经稳定跑了很多年的情况下,不必推翻重来;
  • 如果数据零丢失是硬指标,需要上MySQL半同步复制、PXC、MGR(MySQL Group Replication)这类更现代的方案,配合异步补偿机制;
  • 如果痛点在于读写路由、连接池管理、SQL级分流,那MMM做不好,ProxySQL配合Orchestrator或者MGR会更顺手;
  • 如果业务已经容器化并且跑在Kubernetes上,直接考虑MySQL Operator方案,用声明式Api管理实例生命周期和故障恢复。

我个人的态度是不要迷信新旧,MMM的调度逻辑极其透明,出问题能顺着脚本一层层查下去,这种可排查性在老系统运维里是很珍贵的。换成复杂的新方案,虽然功能强大,但排查问题时黑盒更多,对团队的技术储备要求也高得多。评估的关键永远是两点:这个环境多久没变了,以及出了问题你们能不能自己搞定。

6. 写在最后的经验

最后分享三个我自己在维护MMM集群过程中沉淀下来的习惯吧。

第一,monitor节点单独用一台机器,别跟业务服务混部。给监控脚本的探测间隔、重试次数按照实际网络质量调好,别用默认值硬跑,这个调参过程虽然繁琐,但能避免大量误切换。

第二,每半年至少做一次强制切换演练,用mmm_control move_role把writer从主库挪到备用库再挪回来。演练的意义不只是验证脚本,更是让团队每个人把这个操作练成肌肉记忆。真出故障的时候大家都会紧张,熟练动作可以降低焦虑感。

第三,binlog保留时间一定要拉长,保守一点至少覆盖一个完整备份周期。数据补偿、追查误操作、重建从库,哪件事都离不开binlog,没有日志就等于没有后悔药。

我在实际操作中还会定期在从库上执行数据校验,即使复制链路没报警,也要确认数据没有悄然分叉。技术方案会一直演进,但运维底子永远就三件事,监控、演练、数据完整性,把这三样抓牢,不管用MMM还是以后换别的方案,都不会走偏。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 9:46:52

PX4角加速度数据的获取与应用:提升四旋翼姿态控制性能

很多玩PX4的朋友第一次听到“角加速度数据”时&#xff0c;第一反应都是&#xff1a;这玩意儿不是靠角速度微分就能算出来吗&#xff0c;有什么可稀奇的&#xff1f;我当初也这么想&#xff0c;直到在一次姿态响应的对比测试里&#xff0c;发现同样的PID参数&#xff0c;用不用…

作者头像 李华
网站建设 2026/10/3 9:46:17

OpenShell教程:用经典开始菜单找回Windows高效操作体验

1. 为什么要折腾一个“老古董”开始菜单工具 1.1 从Windows 8到Windows 11&#xff0c;开始菜单带走了多少效率 Windows 8把整个开始菜单换成全屏磁贴那次&#xff0c;我记得很清晰——身边不少同事装的第一个第三方工具就是Classic Shell&#xff0c;也就是OpenShell的前身。…

作者头像 李华
网站建设 2026/10/3 9:44:26

基于NSGA-II的水光互补优化调度:Python实现与Pareto前沿分析

先说结论&#xff1a;如果你手里有一个水电站&#xff0c;旁边还架了一大片光伏板&#xff0c;那每天调度最头疼的事就是——白天光伏出力哗哗往上冲&#xff0c;负荷曲线却不一定跟得上&#xff0c;到了傍晚光伏突然归零&#xff0c;水电站又得在半小时内硬顶上去。单纯以“发…

作者头像 李华
网站建设 2026/10/3 9:43:25

动态住宅IP接入指南:kookeey配置流程与反爬实践

爬虫开发者均匀分布、跨境电商多账号临界封控、市场调研数据源被本地IP限制卡死——这三类场景我接触过太多同类需求&#xff0c;最后几乎都绕回到同一个基础设施问题上&#xff1a;到底怎么搞到稳定、干净、不“撞车”的IP资源。这篇文章就围绕kookeey这个动态住宅IP服务&…

作者头像 李华
网站建设 2026/10/3 9:43:17

PostgreSQL ON CONFLICT源码解析与避坑指南

我清楚记得第一次在9.5的release notes里看见INSERT ... ON CONFLICT时的反应&#xff1a;终于不用再靠规则触发器异常捕获那套歪门邪道来做UPSERT了。从那时起&#xff0c;这套实现就一直是PostgreSQL并发写入场景里的顶梁柱。十年过去&#xff0c;网上仍然有人问"Postgr…

作者头像 李华
网站建设 2026/10/3 9:41:30

公共数据+复现代码:发育生物学单细胞分析全流程指南

上个月我在整理发育生物学课题时&#xff0c;翻到一篇最新的Cell子刊论文。让我印象最深的并不是它发现了多少新细胞类型&#xff0c;而是打开作者公开的GitHub仓库时&#xff0c;发现从GEO下载原始公共数据&#xff0c;到单细胞质控、聚类注释、拟时序分析&#xff0c;再到最后…

作者头像 李华