1. 为什么手动搭建MHA依然值得学1.1 从一次主库宕机说起凌晨两点监控告警响了主库连接数直接掉零。当时我们那套业务用的是单点MySQL没有做任何高可用。运维同学从被窝里爬起来手动切从库、改配置、重启服务前后折腾了四十多分钟。那四十分钟里所有写操作全部失败订单堆积、用户投诉、客服电话被打爆。事后复盘问题不在于“有没有备份”而在于“切换太慢、太依赖人”。这件事之后我开始认真研究MySQL的高可用方案。市面上方案不少但MHAMaster High Availability是我认为最适合中小团队手动搭建、也最能让人真正理解主从切换底层逻辑的一套架构。它不像某些商业方案那样是个黑盒你能清楚看到每一步在干什么出了问题也知道从哪儿查。这篇文章就是把我自己搭MHA的完整过程拆开讲。MHA的核心价值在于主库挂了之后能在几十秒内自动完成故障检测、日志补齐、从库提升、其他从库重新指向新主库这一整套动作把原本需要人工介入几十分钟的活儿压缩到半分钟以内。适合谁看有一定Linux和MySQL基础、想给自己的业务加一层高可用保障、又不想一上来就上复杂分布式方案的开发和运维同学。小白也能跟因为我会把每个参数为什么这么配都讲清楚。1.2 MHA到底解决了什么问题先把概念说透。MHA是一套由日本开发者维护的开源工具集它本身不是MySQL的插件而是运行在管理节点上的一组Perl脚本配合每个MySQL节点上的agent程序工作。它的工作模式分两块Manager节点负责监控和决策Node节点负责执行具体的切换动作。它解决的核心问题是主库单点故障。传统主从架构里主库一挂从库虽然数据还在但没人告诉应用该连哪儿也没人把从库提升成新主库。MHA做的事情就是持续探测主库心跳一旦发现主库不可达立刻从所有从库中挑一个数据最全的通过对比relay log和binlog的位点把它提升为新主库然后把其余从库重新挂到新主库下面最后可选尝试拉起原主库作为新从库。这里有个关键点很多人忽略MHA能最大程度保证数据不丢。它会在切换前从宕机主库上如果SSH还能连上把还没同步到从库的binlog差异部分抓下来补到新主库上。这个动作叫“日志补齐”是MHA区别于简单脚本切换的核心竞争力。当然如果主库彻底宕机、SSH都连不上那这部分差异就补不回来了这也是为什么MHA官方建议配合半同步复制使用。2. 搭建前的架构设计与环境规划2.1 节点角色怎么分配动手之前先把机器规划好不然后面改来改去很痛苦。我这次用的是四台机器虚拟机即可角色分配如下角色主机名说明Manager管理节点mha-manager跑MHA Manager负责监控和切换主库mysql-master初始主库写节点从库1mysql-slave1候选主库优先级高从库2mysql-slave2候选主库优先级低为什么Manager要单独一台因为Manager本身如果和MySQL混部主库挂了可能把Manager一起带崩监控就失效了。管理节点必须独立这是铁律。如果资源紧张Manager可以和某台从库混部但绝不能和主库混部。从库数量建议至少两个。一个从库也能切但没有冗余切换后新架构依然脆弱。两个从库的好处是一个提升为主库另一个继续做从库架构立刻恢复成一主一从还有余量。2.2 版本选择与依赖清单版本这块我踩过坑必须说清楚。MHA对MySQL版本和操作系统都有要求MySQL建议5.7或8.0。MHA 0.58版本对8.0支持较好但要注意8.0默认的caching_sha2_password认证插件MHA可能不认需要改成mysql_native_password。操作系统CentOS 7或Rocky Linux 8都行我用的是Rocky 8。关键是Perl环境要全MHA是Perl写的缺模块会直接报错。SSH互信Manager到所有Node、Node之间都要配SSH免密。MHA靠SSH来远程执行命令和拷贝binlog这个不通后面全白搭。依赖清单我列一下装的时候一次性搞定# 所有节点都需要的依赖 yum install -y perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch \ perl-Parallel-ForkManager perl-Time-HiRes perl-ExtUtils-MakeMaker \ perl-Data-Dumper perl-Digest-MD5注意perl-Parallel-ForkManager这个模块特别容易漏MHA Manager启动时会用它做并行操作缺了会报“Cant locate Parallel/ForkManager.pm”。我第一次搭就是卡在这儿查了半天。2.3 网络与端口规划端口方面MySQL用默认3306MHA Manager本身不监听端口它是主动去连MySQL和SSH。但要注意防火墙Manager要能访问所有Node的3306和22端口Node之间也要能互相访问3306和22。我图省事直接把内网网段全放通了生产环境建议按需开放。主机名解析也要配好/etc/hosts里把四台机器的IP和主机名对应写上。MHA配置里用主机名比用IP更清晰切换日志也好读。3. 主从复制环境的手动搭建3.1 主库配置与账号准备MHA是建立在主从复制基础上的所以第一步得先把一主两从的复制跑通。先改主库的my.cnf[mysqld] server-id1 log-binmysql-bin binlog_formatROW gtid_modeON enforce_gtid_consistencyON log_slave_updatesON这里几个参数解释一下。binlog_formatROW是MHA推荐的因为行复制在数据一致性上更可靠切换时位点计算也更准。gtid_modeON开启GTID好处是切换后从库找位点不用再算binlog文件名和position直接靠GTID自动定位极大降低切换出错概率。log_slave_updatesON是给从库用的让从库也把收到的更新写进自己的binlog这样从库才能作为候选主库继续向下游同步。改完重启MySQL然后创建复制账号和MHA管理账号-- 复制账号 CREATE USER repl% IDENTIFIED BY Repl_2024#Pass; GRANT REPLICATION SLAVE ON *.* TO repl%; -- MHA管理账号需要较大权限 CREATE USER mha% IDENTIFIED BY Mha_2024#Pass; GRANT ALL PRIVILEGES ON *.* TO mha% WITH GRANT OPTION; FLUSH PRIVILEGES;提示MHA管理账号权限要给足因为它要执行CHANGE MASTER TO、RESET SLAVE、SET GLOBAL read_only等操作。生产环境可以细化权限但搭建阶段先给全跑通再收紧。3.2 从库配置与复制启动两个从库的my.cnf类似只是server-id不同[mysqld] server-id2 # slave1用2slave2用3 log-binmysql-bin binlog_formatROW gtid_modeON enforce_gtid_consistencyON log_slave_updatesON read_onlyONread_onlyON让从库只读防止误写。但注意这个参数对拥有SUPER权限的用户无效MHA切换时会临时改这个值所以别用super_read_only否则MHA改不动。配置好后在两个从库上分别执行CHANGE MASTER TO MASTER_HOSTmysql-master, MASTER_USERrepl, MASTER_PASSWORDRepl_2024#Pass, MASTER_AUTO_POSITION1; START SLAVE;MASTER_AUTO_POSITION1就是启用GTID自动定位。执行完用SHOW SLAVE STATUS\G检查确认Slave_IO_Running和Slave_SQL_Running都是YesSeconds_Behind_Master为0。3.3 验证复制与SSH互信复制跑通后在主库建个测试库写点数据看从库能不能同步过来。这一步别省我见过配置看着都对但实际不同步的情况多半是防火墙或账号权限问题。SSH互信配置Manager节点生成密钥公钥分发到所有Node同时每个Node也生成密钥互相分发。简单点可以直接在Manager上对所有节点做免密Node之间也做。验证方法就是ssh mysql-master不输密码能进去。# Manager上执行 ssh-keygen -t rsa -N for host in mysql-master mysql-slave1 mysql-slave2; do ssh-copy-id $host done4. MHA Manager与Node的安装配置4.1 安装MHA软件包MHA分两部分Node节点装mha4mysql-nodeManager节点装mha4mysql-node加mha4mysql-manager。官方没有现成的yum源得下源码包编译或找rpm包。我用的是源码编译虽然慢点但兼容性好。# 所有节点装node tar -zxvf mha4mysql-node-0.58.tar.gz cd mha4mysql-node-0.58 perl Makefile.PL make make install # Manager节点额外装manager tar -zxvf mha4mysql-manager-0.58.tar.gz cd mha4mysql-manager-0.58 perl Makefile.PL make make install装完检查一下命令在不在which masterha_manager、which masterha_check_ssh。这两个命令后面要用。4.2 编写Manager配置文件这是整个搭建的核心。在Manager节点上建/etc/mha/app1.cnf[server default] manager_workdir/var/log/mha/app1 manager_log/var/log/mha/app1/manager.log usermha passwordMha_2024#Pass ssh_userroot repl_userrepl repl_passwordRepl_2024#Pass ping_interval2 master_binlog_dir/var/lib/mysql [server1] hostnamemysql-master candidate_master1 check_repl_delay0 [server2] hostnamemysql-slave1 candidate_master1 check_repl_delay0 [server3] hostnamemysql-slave2 no_master1逐项解释。ping_interval2是每2秒探测一次主库探测越频繁切换越快但网络抖动时容易误判2秒是个平衡点。master_binlog_dir告诉MHA去哪个目录找binlog路径必须和实际一致否则日志补齐会失败。candidate_master1表示这个节点可以作为候选主库。我给两个从库都设了但slave1的优先级更高MHA按配置顺序选。check_repl_delay0是关闭复制延迟检查意思是即使这个从库有延迟也允许它当主库。这个参数有争议设0切换快但可能丢数据设1更安全但延迟大时切不了。我建议只在延迟可控的环境设0否则还是保守点。no_master1表示slave2永远不参与主库竞选纯做从库。这样架构更清晰。4.3 配置校验与启动配置写完别急着启动先用MHA自带的检查工具验证# 检查SSH互信 masterha_check_ssh --conf/etc/mha/app1.cnf # 检查复制状态 masterha_check_repl --conf/etc/mha/app1.cnfcheck_ssh会遍历所有节点两两测试全绿才行。check_repl会检查主从复制、账号权限、binlog格式等输出“MySQL Replication Health is OK”才算过。这两个检查我强烈建议每次都跑。我遇到过check_repl报“binlog format is not ROW”的情况回头查才发现有个从库的my.cnf没改全。这种问题不检查直接启动切换时才发现就晚了。启动Managernohup masterha_manager --conf/etc/mha/app1.cnf \ --remove_dead_master_conf \ --ignore_last_failover \ /var/log/mha/app1/manager.out 21 --remove_dead_master_conf表示切换后把死掉的主库配置从文件里删掉避免下次启动报错。--ignore_last_failover是忽略上次的failover记录否则MHA默认8小时内不重复切换。这两个参数在测试阶段很有用。5. 故障切换实测与日志解读5.1 模拟主库宕机搭好了不测等于没搭。测试方法很简单在Manager运行状态下直接把主库的MySQL进程杀掉或者更彻底点关机。# 在mysql-master上 systemctl stop mysqld然后盯着Manager日志/var/log/mha/app1/manager.log。正常的话几秒内就会看到“Ping(SELECT) failed”之类的探测失败记录接着开始failover流程。5.2 切换过程逐步拆解MHA的切换流程大致分这几个阶段日志里能清楚看到检测阶段连续多次ping失败后判定主库宕机。日志补齐阶段尝试SSH连原主库把差异binlog拷到Manager再应用到新主库。如果SSH连不上跳过这步并记录警告。选主阶段从候选从库中选一个对比GTID或位点选数据最新的。提升阶段对新主库执行STOP SLAVE、RESET SLAVE ALL、SET GLOBAL read_onlyOFF把它变成可写主库。重定向阶段其余从库执行CHANGE MASTER TO指向新主库重新启动复制。收尾阶段写failover记录Manager进程退出MHA Manager默认切换后自己停掉需要配合守护脚本重新拉起或手动重启。整个流程实测下来从杀进程到新主库可写大概20到40秒。比人工快太多了。5.3 切换后验证与恢复切换完成后登录新主库原来的slave1确认SHOW MASTER STATUS; SELECT read_only; -- 应该是0再登录slave2SHOW SLAVE STATUS\G确认它已经指向新主库且复制正常。原主库修好后可以手动把它重新加入集群作为从库。步骤是启动MySQL执行CHANGE MASTER TO指向新主库START SLAVE。注意原主库上可能有新主库没有的数据宕机前没同步的所以重新加入前最好确认数据一致性或者干脆用备份重建。注意MHA切换后Manager进程会退出这是设计如此。生产环境要用masterha_manager配合systemd或supervisor做守护让它退出后自动重启否则第二次故障就没人管了。6. 常见问题排查与避坑经验6.1 高频报错速查表报错信息原因解决Cant locate Parallel/ForkManager.pm缺Perl模块装perl-Parallel-ForkManagerSSH connection failed互信没配好重新分发公钥检查authorized_keys权限binlog format is not ROWbinlog格式不对改my.cnf并重启MySQLMySQL Replication Health is NOT OK复制断了或账号错查SHOW SLAVE STATUS核对账号密码Failed to get master binlogbinlog路径配错核对master_binlog_dir与实际路径Authentication plugin caching_sha2_passwordMySQL8认证插件问题改用户为mysql_native_password6.2 几个容易翻车的细节第一个坑SSH互信只配了单向。MHA不仅Manager要连NodeNode之间也要互相连比如新主库要从原主库拷binlog。我第一次只配了Manager到Node切换时报“SSH to mysql-master failed”。后来补全了所有节点两两互信才通过。第二个坑GTID和位点混用。如果主从用GTIDMHA配置里也要相应调整否则切换时位点计算会出错。我建议要么全GTID要么全位点别混。第三个坑read_only和super_read_only。前面提过MHA切换要改read_only如果开了super_read_only连SUPER用户都写不了切换会卡住。生产环境如果非要开super_read_only得在MHA配置里做特殊处理。第四个坑Manager单点。MHA Manager本身也是单点它挂了就没人监控了。生产环境可以考虑用两个Manager做互备或者用keepalived给Manager做VIP。这个进阶话题以后可以单独聊。6.3 我的实操心得搭MHA最耗时的不是配置而是排查环境问题。我的建议是先把主从复制跑稳再装MHA装完先跑check_ssh和check_repl两个都过了再启动启动后立刻做一次切换测试别等真出事才发现切不了。另外MHA的日志非常详细出问题先看manager.log里面会告诉你卡在哪一步。我大部分问题都是靠日志定位的比瞎猜快得多。还有一点MHA不是银弹。它能解决主库宕机的自动切换但解决不了数据延迟导致的丢失除非配合半同步也解决不了脑裂需要额外机制。把它当成高可用体系里的一环而不是全部。最后分享个小技巧测试切换时可以用masterha_master_switch --master_statealive做在线切换不用真杀进程。这个命令能在主库活着的时候演练切换对生产环境做维护窗口特别有用。