ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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 VIPstandby 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管理是它最值钱的能力。一个集群里通常会有两类VIPwriter VIP整个集群同一个时刻只能有一个节点持有它指向当前提供写入的主库reader VIP可以同时挂在多个从库上供只读业务、报表查询、分析任务连接。monitor在认定writer故障后会触发一组切换动作集合大致是尝试从故障节点上移除writer VIP把writer VIP绑定到standby master节点在备用节点上关闭read_only让应用可以写入通知各个从库重新执行复制指向操作把数据源切到新的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初始是writernode2是standbynode3、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 2node2的配置除了server-id2auto_increment_offset2之外其余基本一致。这两个自增参数是双主配置里最容易犯错的点offset决定了各自主键从哪个起点开始生成increment限制了同一批连续ID分配给不同节点的间隔。设成offset1和offset2increment2就能保证node1生成奇数node2生成偶数即使两边同时写入也不会出现主键冲突。还要强调几个容易踩的配置点log_slave_updates1必须开。它的作用是把从库通过中继日志回放的事件也写进这台机器的binlog。如果不开备用节点作为从库收下的binlog不会产生自己的binlog后续继续向后传递复制链时就会断掉binlog_format用ROW更稳妥。在主主复制和双节点互备的场景里ROW格式对数据一致性最好也能避免某些SQL函数在两端执行结果不一致的问题read_only参数不要手工固定写在配置里因为MMM会通过agent脚本动态切换read_only状态如果配置文件里写死切换时就会打架。复制账号这一步不能省。在两个主节点和所有从库上都要创建同一个复制账号CREATE USER repl% IDENTIFIED BY Replstrong123; 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.confmonitor端私有配置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 Replstrong123 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。后来总结出正确顺序先在所有DB节点上启动agent服务service mysql-mmm-agent start再启动monitor节点的监控服务service mysql-mmm-monitor start最后用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角色已经漂移到node2reader VIP也重新分配完毕。整个过程没有人干预业务写入短暂中断后自动恢复。把这次切换拆开来看monitor做了这几件事连续若干次探测失败后把node1判死调用node1上的agent脚本尝试移除VIP但由于主机已经关机这一步是失败的不过不影响后续在node2上执行绑定writer VIP并关闭read_only让node3、node4重新执行change master把数据源切换到node2更新mmm_control记录把node2标记为writer。这里就暴露了一个关键问题如果node1不是彻底宕机而只是网络隔离那么agent脚本可能依然无法执行移除VIPnode1网卡上的VIP就还挂着。业务流量按照VIP的ARP关系仍然会被引到node1导致切换看起来做了但实际业务还是连不上。这个场景非常隐蔽需要提前在网络层或交换机层配置防隔离手段比如配合防火墙脚本清理ARP表或者在云平台安全组里隔离故障节点。MMM的Perl脚本本身对这类脑裂场景的防御很弱必须靠外围保障补齐。4.2 旧主恢复后的回切流程与数据补偿旧主修复后重新加回集群不会自动抢回writer角色。MMM默认会让它作为standby节点上线但复制关系必须人工处理。我的习惯是不直接复用旧主的binlog继续复制原因在于旧主宕机期间可能有日志空洞直接change master可能让数据链出现断层。正确做法是在新主node2上做全量备份恢复到旧主node1再重新建立复制关系。步骤归纳如下在新主node2上执行全量备份恢复到旧主node1在node1上执行CHANGE MASTER指向node2启动复制线程用mmm_control show确认node1已被识别为standby角色为reader之一如果业务需要回切到原主选择一个低峰窗口执行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、MGRMySQL Group Replication这类更现代的方案配合异步补偿机制如果痛点在于读写路由、连接池管理、SQL级分流那MMM做不好ProxySQL配合Orchestrator或者MGR会更顺手如果业务已经容器化并且跑在Kubernetes上直接考虑MySQL Operator方案用声明式Api管理实例生命周期和故障恢复。我个人的态度是不要迷信新旧MMM的调度逻辑极其透明出问题能顺着脚本一层层查下去这种可排查性在老系统运维里是很珍贵的。换成复杂的新方案虽然功能强大但排查问题时黑盒更多对团队的技术储备要求也高得多。评估的关键永远是两点这个环境多久没变了以及出了问题你们能不能自己搞定。6. 写在最后的经验最后分享三个我自己在维护MMM集群过程中沉淀下来的习惯吧。第一monitor节点单独用一台机器别跟业务服务混部。给监控脚本的探测间隔、重试次数按照实际网络质量调好别用默认值硬跑这个调参过程虽然繁琐但能避免大量误切换。第二每半年至少做一次强制切换演练用mmm_control move_role把writer从主库挪到备用库再挪回来。演练的意义不只是验证脚本更是让团队每个人把这个操作练成肌肉记忆。真出故障的时候大家都会紧张熟练动作可以降低焦虑感。第三binlog保留时间一定要拉长保守一点至少覆盖一个完整备份周期。数据补偿、追查误操作、重建从库哪件事都离不开binlog没有日志就等于没有后悔药。我在实际操作中还会定期在从库上执行数据校验即使复制链路没报警也要确认数据没有悄然分叉。技术方案会一直演进但运维底子永远就三件事监控、演练、数据完整性把这三样抓牢不管用MMM还是以后换别的方案都不会走偏。
返回列表