如果你手上维护着一套 MySQL 多节点集群,早晚会碰到一个绕不开的名字:MMM。MMM(Multi-Master Replication Manager for MySQL)是一套老牌的 MySQL 复制高可用管理工具,它解决的核心问题不是“让两个 MySQL 同时对外写”,而是“让两个 MySQL 保持主主复制关系,同时用一套监控、VIP 漂移和角色切换机制,保证某一时刻只有一台真正承担写压力”。说得直白点,它是控制面工具,数据复制本身仍然靠 MySQL 原生的 master-master 复制来完成,MMM 负责的是“谁该活着、谁该写、坏了怎么换”这三件事。
这篇文章适合两类人看:一类是还在维护老 MySQL 5.x 集群、被 MMM 的切换逻辑折腾过的 DBA;另一类是刚开始做高可用选型,想理解“监控节点 + Agent + 虚拟 IP”这套高可用设计模板的新同学。我会从核心原理讲到一套 4 节点实战部署,再把多年积累的故障排查经验按“症状—原因—动作”的方式整理出来,尽量让你读完就能上手,而不是只记住几个概念。
1. MMM 到底是什么:定位、组件与设计目标
1.1 先搞清楚:MMM 不是“双写”工具
网上很多文章把 MMM 说成“双主双写方案”,这是一个很容易误导人的说法。MMM 虽然在拓扑上是 master-master,两台 MySQL 互为对方的 Slave,但它通过active_master_role这个配置把“写角色”收拢成一个:同一时刻只允许一台机器对外提供写服务,另外一台要么待命、要么只承担读流量。
可以打个比方:两台车共用一条车道,平时只有一台上路,另一台在停车场待命,出事了才顶上。MMM 做事就是这两件事——维护两台车之间的同步关系(复制),以及控制“哪台车可以上路”(VIP 和 read_only)。它刻意避免双写,因为 MySQL 原生复制没有冲突解决机制,一旦两边同时产生同主键数据,1062 错误会立刻打断复制,反而比单主更危险。
1.2 四个核心组件与它们的分工
MMM 在实际部署里就是你熟悉的 agent/monitor 模式,整个体系由四个部分组成。
| 组件 | 运行位置 | 职责 | 默认端口 |
|---|---|---|---|
| mmmd_agent | 每台 MySQL 节点 | 执行本地检查、切换 VIP、修改 read_only、报告复制状态 | 9988 |
| mmmd_mon | 独立监控节点 | 连接所有 agent、维护检查历史、判定故障、执行角色切换 | 9989 |
| mmm_control | DBA 命令行 | 查看集群状态、手动上下线节点、手动搬移角色 | - |
| mmm_backup | 辅助工具 | 基于 LVM 快照/LVM 卷的在线备份 | - |
配置上,所有节点共享一个/etc/mmm_common.conf(定义主机、角色、VIP),每台数据库节点另外有/etc/mmm_agent.conf(告诉 agent 自己是哪台、用哪块网卡),监控节点上有/etc/mmm_mon.conf(定义 monitor 自己、数据库账号等)。早期版本还有/etc/mysql-mmm/*.conf这种路径,不同发行版放的位置略有差异,但核心三件套是不变的。
1.3 这套设计到底解决了什么问题
MySQL 要做高可用,最朴素的做法是“VRRP/keepalived 飘一个 VIP”,但这里有个坑:MySQL 是有状态的服务,VIP 飘过去之后,新主上如果缺少旧主最后的 binlog 位点,数据就悄悄丢了,应用却毫无感知。MMM 的价值就在于它把“复制状态”和“对外访问路径”绑在了一起:切换 VIP 之前,它会先考虑复制位点、把从库重指向新主、把待命主从只读改成读写。它管的不只是 IP,而是一整套“角色—复制—访问”的一致性。
和 DRBD 这类块级复制方案比,MMM 是在 MySQL 逻辑层工作,不管磁盘块同步,所以网络要求没那么苛刻,也不会因为块设备不一致把整个数据库搞挂。代价是它管不住 GTID、管不了跨机房网络分区,这些东西后面我会详细讲。
2. 技术原理:监控、检测、切换是怎么串起来的
2.1 眼睛和手:Agent 与 Monitor 的通信模型
MMM 的通信模型很简单:monitor 主动拉取,agent 不推送。mmmd_mon每隔 1 到 2 秒(由ping_interval、check_period控制)通过 TCP 9988 连接每台机器上的mmmd_agent,发送检查指令;agent 在自己的机器上执行完检查后返回结构化结果。检查项包括 MySQL 实例是否可连接、复制线程状态与延迟、磁盘剩余空间等。
这里有个值得理解的设计点:monitor 为什么不直接连每台 MySQL 的 3306 去查状态,非要绕一道 agent?因为 agent 不只是“眼睛”,还是“手”。执行角色切换时,需要有人在本地加/删 VIP、执行SET GLOBAL read_only、执行CHANGE MASTER。这些动作如果都要 monitor 远程 SSH 去做,一方面权限模型会非常混乱,另一方面多一层跳板就多一层故障。所以 agent 承担本地执行者的角色,monitor 只做决策和调度。
agent 和 monitor 这两个进程都不吃资源,Perl 写的守护进程,每台机器上常驻内存占用很小,但要注意它们之间的网络质量。负责心跳的链路如果经常抖动,直接结果就是误报和脑裂,所以生产环境我强烈建议把 9988/9989 放在独立管理网段里,别和业务流量混在一起。
2.2 检查项与故障判定:为什么不会动不动就切换
判定一台 MySQL “死亡”并不是一次检查失败就下结论。MMM 在 monitor 侧维护一个检查历史,默认需要连续若干次失败(max_backlog默认值是 2,配合check_period大约几秒)才把节点标记为 offline。这个设计是为了防止网络瞬时抖动导致集群反复切换——这是高可用方案最常见也是最致命的毛病:平时好好的,一个交换机 ARP 表刷新的功夫,VIP 飘过去了,应用连接全断开。
agent 返回的检查结果也分层次:如果 agent 完全连不上,monitor 只能判断“主机失联”;如果 agent 能连上但 MySQL 的 ping 检查失败,说明“机器活着,MySQL 挂了”;如果复制位点落后太多,agent 会报告“复制状态异常”。这三种故障的后果完全不同,排查时看日志里记录的具体检查项,比自己瞎猜要快得多。
值得记住一个经验值:检测阶段通常要 2 到 6 秒,再加上切换动作(摘 VIP、升角色、发免费 ARP、改从库指向)一般需要几秒到十几秒,所以一个健康 MMM 集群的实测 RTO 通常在十几秒到几十秒,而不是秒级。如果谁跟你说 MMM 能做到几秒切换,那基本没做过故障演练。
2.3 角色、VIP 与“同一时刻只有一个写主”
MMM 的一切动作都围绕“角色”展开。最常见的四节点架构里,角色定义大概是这样的:
role { writer } mode { exclusive } vip { 192.168.10.100 } role { reader } mode { balanced } vip { 192.168.10.101, 192.168.10.102 }writer角色是exclusive,意味着整个集群同一时刻只允许一台机器持有写 VIP;reader角色是balanced,多台从库可以各持一个读 VIP。每台主机上配置的roles代表它“可以承担”的角色,比如 db1 配了writer,db2 配了writer, reader,db3 和 db4 配了reader。
VIP 的切换机制很有意思:它不依赖 VRRP/keepalived。agent 在本地网卡上通过 ip alias 直接加地址(老版本是ifconfig eth0:1,新一点的是ip addr add),然后发一个免费 ARP,让交换机刷新 ARP 表。这意味着只要二层网络可达,MMM 就能完成 VIP 漂移,不需要额外的协议支持。但也正因为没有 VRRP 的“抢占仲裁”,当 monitor 和某台旧主的 agent 失联时,旧主上的 VIP 可能并没有真正被移除——这就是脑裂的温床,后面排障部分我会重点展开。
2.4 从故障发生到写流量恢复:完整切换流水线
一次完整的自动故障切换,如果你打开 monitor 日志,看到的动作序列大致是这样的:
- 故障发生,monitor 对故障节点的连续检查开始失败。
- 达到判定阈值,monitor 在内存状态里把该节点标为 offline。
- 尝试让故障节点的 agent 移除其持有的 VIP。如果 agent 已经不可达,这一步会跳过——故障歧义就在这里产生。
- 从“可以承担 writer 角色”且状态 online 的节点里选出新写主。MMM 会参考候选者上报的复制位点,确认它和旧主之间的差距是否在可接受范围。
- 在新写主上执行角色激活:
SET GLOBAL read_only=0,添加写 VIP,发送免费 ARP。 - 把其余从库重指向新写主(
CHANGE MASTER)。 - 读 VIP 在剩余的 reader 节点之间重新分配,更新角色表。
注意第 4 步的“位点差距”。MMM 管不了 MySQL 异步复制丢数据的本质:如果旧主已经写入了新事务但还没同步给新主,切换后这些事务就丢了。所以在 master-master 这两台之间,生产上务必打开半同步复制(MySQL 5.5 引入的 semi-sync)来缩小这个窗口,否则一切“高可用”都建立在一个会静默丢数据的底座上。
再强调一个反直觉的细节:MMM 切换后,挂在后面的纯从库(比如 db3、db4)未必会自动改好复制指向,不同 fork 的处理逻辑差异很大。我见过太多团队在演练时才发现,写 VIP 切走了,但从库还在追一个已经死掉的主库,读流量全读到了过期数据。这件事必须写进你的故障演练清单。
3. 实践架构:一套 4 节点 MMM 集群的完整搭建
3.1 拓扑与 IP 规划
下面这套拓扑是我在生产里用过比较标准的 4 节点方案:两台 Master 组成主主复制,两台 Slave 挂在当前写主下面承担读流量,外加一台独立的监控节点。
| 节点 | 业务 IP | 角色定义 | 说明 |
|---|---|---|---|
| monitor | 192.168.10.10 | 监控节点 | 只跑 mmmd_mon 和 mmm_control |
| db1 | 192.168.10.11 | writer | 初始写主,Master-A |
| db2 | 192.168.10.12 | writer, reader | 写主候选,Master-B |
| db3 | 192.168.10.13 | reader | 只读从库,Slave-A |
| db4 | 192.168.10.14 | reader | 只读从库,Slave-B |
IP 分配上再额外划出三个漂移地址:写 VIP192.168.10.100,两个读 VIP192.168.10.101和192.168.10.102。这三段地址只作为服务入口,应用连接字符串里永远写 VIP,不许写真实服务器 IP,否则一切换应用就断。
如果你条件允许,我给一个额外建议:网络至少分两层。业务 VLAN 给应用、读 VIP、写 VIP 用;管理 VLAN 给 monitor 到 agent 的 9988、DBA 的 SSH 用。控制面网络越干净,误报和脑裂的概率越低。这个细节在机房网络拥塞时能救你命。
3.2 MySQL 层准备:配置、账号与初始化复制
先配数据库层。写主(db1、db2)的my.cnf关键项如下:
[mysqld] server-id = 1 log-bin = mysql-bin log-slave-updates = 1 binlog-format = ROW expire_logs_days = 7 skip-name-resolve = 1 auto_increment_increment = 2 auto_increment_offset = 1db2 上server-id = 2,auto_increment_offset = 2。两个参数的组合是为了避免主主复制下自增主键撞车:一个节点生成奇数主键,另一个生成偶数主键。很多人搭建双主时最容易漏掉的就是这两行,等出现 1062 才知道疼。
从库 db3、db4 的配置相对简单:
[mysqld] server-id = 3 log-bin = mysql-bin log-slave-updates = 1 relay-log = relay-bin read_only = 1注意从库也开了log-bin和log-slave-updates,一是为了以后把从库提升为主库时方便级联,二是 MMM 判断复制状态需要它在本地有完整的 binlog 序列。read_only = 1是兜底保险:哪怕 MMM 没来得及切状态,从库也不会被误写。
账号方面,准备两个基础用户:
CREATE USER 'repl'@'%' IDENTIFIED BY 'Repl@Pass123'; GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%'; CREATE USER 'mmm_admin'@'%' IDENTIFIED BY 'Mmm@Pass123'; GRANT SUPER, PROCESS, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'mmm_admin'@'%'; FLUSH PRIVILEGES;复制账号repl供 MySQL 原生复制用;mmm_admin供 agent 和 monitor 做状态检查、切换 read_only 用。没有 SUPER 权限,MMM 在 5.7 及以后版本的 MySQL 上根本切不动 read_only,这是一个非常隐蔽的“配置看起来都对,一切换就失败”的原因。
初始化主主复制时,我的做法是先锁定基准点再各自 CHANGE MASTER,避免两边从不同位置开始造成数据错乱。简单流程:把 db1 的数据用mysqldump或 xtrabackup 刷一份到 db2,记录当时的 binlog 位点,然后:
-- db2 上执行 CHANGE MASTER TO MASTER_HOST='db1', MASTER_USER='repl', MASTER_PASSWORD='Repl@Pass123', MASTER_LOG_FILE='mysql-bin.000012', MASTER_LOG_POS=154; START SLAVE;db1 上同样执行指向 db2 的CHANGE MASTER,位点用 db1 初始化数据时对应的坐标。双向拉起来之后,用SHOW MASTER STATUS和SHOW SLAVE STATUS\G确认两边Seconds_Behind_Master都是 0,再继续装 MMM。
3.3 安装 MMM 与三个配置文件
Debian/Ubuntu 上直接用包安装最省事:
apt install mysql-mmm-agent mysql-mmm-monitor mysql-mmm-toolsCentOS 系老版本可以走 EPEL 或源码安装。源码安装需要 Perl 模块:DBI、DBD::mysql、Log::Log4perl、Proc::Daemon、Net::Ping等,版本越老依赖越烦,不建议在生产浪费时间,优先找发行版包。装完后每台数据库节点启动 agent,监控节点启动 monitor:
service mysql-mmm-agent start service mysql-mmm-monitor start接下来是重头戏,三个配置文件。
/etc/mmm_common.conf,所有节点完全一致:
cluster_name { mysql_mmm } active_master_role { writer } host { db1 } ip { 192.168.10.11 } port { 3306 } pid { /var/run/mysqld/mysqld.pid } status { online } roles { writer } host { db2 } ip { 192.168.10.12 } port { 3306 } pid { /var/run/mysqld/mysqld.pid } status { online } roles { writer, reader } host { db3 } ip { 192.168.10.13 } port { 3306 } pid { /var/run/mysqld/mysqld.pid } status { online } roles { reader } host { db4 } ip { 192.168.10.14 } port { 3306 } pid { /var/run/mysqld/mysqld.pid } status { online } roles { reader } role { writer } mode { exclusive } vip { 192.168.10.100 } role { reader } mode { balanced } vip { 192.168.10.101, 192.168.10.102 }/etc/mmm_agent.conf,每台数据库节点上内容不同:
default_interface { eth0 } this_dbms { db1 }db2 上把this_dbms改成db2,以此类推。default_interface一定要填实际承载 VIP 的那块网卡名,填错了免费 ARP 发不出去,VIP 看起来加上了但外部访问不通,这是非常经典的“状态正常、服务不正常”案例。
/etc/mmm_mon.conf,只在监控节点上:
monitor_host { 192.168.10.10 } monitor_lifetime_interval { 1 } monitor_pidfile { /var/run/mmmd_mon.pid } check_db_login { mmm_admin } check_db_pass { Mmm@Pass123 }注意不同 fork 对这段数据库账号的配置项命名不完全一样,有的版本还区分master_db_login和check_db_login。以你拿到的发行版 sample 文件为准,但原则只有一个:monitor 和 agent 用来查复制状态、切 read_only 的账号必须有足够权限。
配置文件放好后,按“先数据库节点 agent、再监控节点 monitor”的顺序启动。启动后用mmm_control show验证,正常时会看到类似下面的示意输出:
db1(192.168.10.11): master(Online) db2(192.168.10.12): standby(Online) db3(192.168.10.13): slave(Online) db4(192.168.10.14): slave(Online) 写VIP 192.168.10.100 -> db1 读VIP 192.168.10.101 -> db3 读VIP 192.168.10.102 -> db4看到写 VIP 正确落在 db1 上,一切才算就位。
3.4 日常运维与切换演练
MMM 管理命令不多,但每一个都很关键。日常最常用的mmm_control子命令如下:
| 命令 | 作用 | 典型场景 |
|---|---|---|
mmm_control show | 查看节点状态和 VIP 归属 | 日常巡检 |
mmm_control checks | 查看 monitor 对每个节点的检查明细 | 排查误报 |
mmm_control set_online <host> | 把节点置为在线 | 故障恢复后回归集群 |
mmm_control set_offline <host> | 把节点置为离线 | 计划维护 |
mmm_control move_role <host> <role> | 手动把角色搬到指定节点 | 计划内切换、负载调整 |
强烈建议每季度做一次“计划内切换演练”,流程很简单:先在应用低峰期用mmm_control move_role db2 writer把写角色从 db1 搬到 db2,观察写 VIP 是否迁移、db3/db4 的从库指向是否自动更新,再反向搬回来。演练时让开发配合做一轮“建表、写入、查询”的冒烟测试,确认 VIP 切换后应用连接不需要人工干预。
我见过太多团队搭好 MMM 后一次没演练过,等真出故障时才发现:监控节点磁盘满了、agent 进程被 OOM 干掉、防火墙策略被人改过——所有“看起来没问题”都在关键时刻变成问题。演练不是走个过场,它是唯一能暴露这些隐性故障的手段。
4. 常见问题与排查实录
4.1 Agent 联不通、节点被误判 offline
这是 MMM 集群里最高频的问题:mmm_control show里某节点显示 offline,但 MySQL 明明活着。处理思路很固定,按顺序查四件事:
先看进程:ps -ef | grep mmmd_agent,确认 agent 活着;再看端口:ss -lntp | grep 9988,确认监听;然后从监控节点测试链路:telnet 该节点IP 9988,通则网络没问题;最后翻日志:tail -100 /var/log/mysql-mmm/mmmd_agent.log。
如果链路不通,多半是 firewall 或者管理网段路由被改了;如果链路通但 agent 状态异常,通常是 agent 的本地检查连不上 MySQL,比如 MySQL 的 socket 路径和pid配置对不上、或者mmm_admin密码被改过。问题解决后执行mmm_control set_online <host>把节点拉回,再观察两三个检查周期确认不再掉线。
注意:
set_online只是让 MMM 恢复对该节点的管理,它不会自动修复复制。节点回归集群后,必须自己确认复制关系和 VIP 归属都正确,再对外提供流量。
4.2 故障切换没发生,或切到一半失败
切换没触发,优先翻/var/log/mysql-mmm/mmmd_mon.log,看故障时段 monitor 记录的检查历史。最常见的三类原因:
第一,连续失败次数没达到阈值。max_backlog配置得太大,或者 MySQL 只是“间歇性不可用”,每次失败的间隔被在线结果打断,monitor 永远不判定 offline。这种情况要在测试环境里压一下阈值,但别压太狠,否则一次 GC 停顿或网络抖动就触发切换。
第二,权限不足。切换最后一步要在新主上执行SET GLOBAL read_only=0,如果mmm_admin缺 SUPER 权限,日志里会出现明确的权限报错,整个切换流程卡在最后一步。MySQL 5.7 之后对动态变量的权限检查更严格,这个问题在老文档里基本没提过。
第三,候选主复制位点落后太多。MMM 升主前会参考候选主上报的复制位置,如果候选主落后旧主一大截,它可能拒绝直接提升,或者提升之后丢大量数据。处理办法是给两台主之间开半同步复制,并且确保从库不会长时间落后。
4.3 脑裂:写 VIP 同时出现在两台机器,怎么办
脑裂是 MMM 最危险的事故形态,典型触发场景是“半故障”:db1 并没有宕机,只是 monitor 到 db1 的网络断了,monitor 判断 db1 离线并执行切换,把写 VIP 加到了 db2 的网卡上。但 db1 的网卡上的写 VIP 并没有被移除(monitor 连不上它的 agent,摘不掉),于是两台机器都认为自己才是写主,应用连哪个 VIP 都能写,数据开始分叉。
遇到这种情况我的第一反应不是“查日志”,而是“止血”:立刻把其中一台的网络隔离掉。最干脆的做法是远程关机,或者把承载 VIP 的网卡 down 掉,优先保证整个集群恢复“唯一写主”的状态,哪怕短暂不可用也比数据双写强。等唯一写主确认后,再回来排查到底是哪条链路断了、哪个 VIP 没摘干净。
预防脑裂靠三件事:一是把 monitor 到 agent 的心跳网络单独隔离,降低控制面故障概率;二是配置 fencing 脚本,比如通过 IPMI 远程电源控制在切换时强制断电旧主,做到真正的 STONITH;三是永远不要把“假设旧主已经死了”当作安全前提,一切以“我已经确认旧主无法对外提供服务”为准。
4.4 复制中断、1062 主键冲突、从库落后
复制中断排在第一位的原因是 1062 主键冲突。出现时先用SHOW SLAVE STATUS\G看Last_SQL_Error,如果确实是重复主键,先确认两边的数字。应急手段是STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; START SLAVE;跳过这条错误,但这只是让复制继续走,冲突数据还得事后手工补齐——跳过错误不会让数据变对。
预防 1062 的根本手段就是前面说的auto_increment_increment和auto_increment_offset,以及“永远只通过写 VIP 写入”。很多冲突其实是应用绕过 VIP 直连服务器 IP 写出来的,数据库层再努力也拦不住这种违规操作。
从库落后超过预期,先看磁盘和 I/O:relay log 目录空间不足、磁盘 IO 被打满都会让 SQL 线程停滞。MMM 的 agent 会检查本地磁盘剩余空间,但阈值是死的,不会替你做容量规划,该扩容扩容,该清 binlog 清 binlog。清理 binlog 用PURGE BINARY LOGS,千万别手滑删文件,那会导致从库彻底断链。
4.5 排障速查表
下面这张表是我每次进 MMM 集群排障都会先扫一遍的清单,直接照做能省大量时间:
| 排查项 | 命令/路径 | 说明 |
|---|---|---|
| agent 日志 | /var/log/mysql-mmm/mmmd_agent.log | 本地检查、VIP 操作记录 |
| monitor 日志 | /var/log/mysql-mmm/mmmd_mon.log | 故障判定、切换流程记录 |
| agent 端口 | ss -lntp | grep 9988 | 每台数据库节点 |
| monitor 端口 | ss -lntp | grep 9989 | 监控节点 |
| 复制状态 | SHOW SLAVE STATUS\G | 看 IO/SQL 线程、延迟、报错 |
| 节点状态 | mmm_control show | VIP 归属和节点状态 |
| 配置目录 | /etc/mmm_common.conf等 | 变更前必备份 |
日志文件的老化策略容易被忽略:MMM 用的是 Log::Log4perl,默认配置下日志可能无限增长。建议在日志轮转(logrotate)里把它加上,否则监控节点磁盘满了之后,monitor 连写日志都写不动,整个控制面就瘫了——这个坑我踩过不止一次。
5. 选型边界与我的实际体会
5.1 什么场景还适合 MMM
MMM 毕竟是十几年前的设计,适用边界非常清晰:MySQL 5.5/5.6,使用传统 file+pos 位点复制,网络环境单一(同机房、同 VLAN),团队能接受 Perl 老工具链,并且愿意定期做故障演练。这种场景下 MMM 反而很“皮实”:代码简单、行为可预期、日志直白,一个熟练 DBA 完全能掌控得住。
如果你的环境是 MySQL 8.0、开了 GTID、甚至还想跨机房容灾,就不要硬上 MMM 了。GTID 模式下复制状态判断逻辑完全不同,MMM 的位点比对和 CHANGE MASTER 逻辑会水土不服,切换结果没人敢打包票。技术选型最忌讳“用情怀对抗版本”,老工具能用是福气,但时代变了就该换车。
5.2 MySQL 8.0/GTID 时代的替代方案
这些年我用过的主流替代品里,简单对比一下:
| 方案 | 适用场景 | 优点 | 短板 |
|---|---|---|---|
| MHA | 单主多从,自动提升 | 成熟、脚本可控、RTO 秒级到十秒级 | 无 VIP 漂移,需配合脚本;GTID 支持一般 |
| orchestrator | 复制拓扑管理 | 可视化、自动修复复制链路、支持 GTID | 自身也要做 HA,否则成为新的单点 |
| MySQL InnoDB Cluster / MGR | 新项目首选 | 官方方案、GTID 原生、自动选主 | 架构约束多,老业务改造成本高 |
| MySQL Router | 应用接入层 | 读写分离、故障感知 | 不做复制管理,只做路由 |
我的实际倾向是:新系统直接考虑 InnoDB Cluster/MGR,旧系统想稳妥平滑就上 MHA 或 orchestrator。MMM 更适合作为理解“agent + monitor + VIP + 角色”这套高可用思想的教材,以及继续服务那些已经稳定运行多年的老集群。
5.3 几句踩坑踩出来的心得
带老集群那几年,MMM 留给我最大的财富其实不是工具本身,而是一套排障习惯:遇到任何高可用异常,先画三张图——心跳是怎么打的、VIP 归谁管、角色变更要动哪些 SQL。MMM 把这三件事逼得我必须背得滚瓜烂熟,后来换到任何方案都受益。
最后分享两个小技巧。第一,monitor 节点上务必做好防止 OOM 的配置,它本身很轻,但一旦和别的业务进程混部署,内存被抢走后 agent 心跳照常、监控进程却僵死,整个集群会变成“看起来正常、实际无人值守”的状态。第二,每次对 MMM 配置做变更前,先把/etc/mmm_common.conf备份一份,并且按“改一台、观察一个检查周期、再改下一台”的节奏推进。这个工具不像新方案那样有完善的在线热更新机制,一次性大改配置的结果往往是全部节点状态异常,还得一个个手动拉回。稳一点,慢一点,比什么都强。