摘要
MySql-MHA的构建过程
本文基于mysql-8.0.30,mha4mysql-0.58-0.el7.centos
MHA 0.58 已停更多年,只适合当时的 MySQL 8.0;不适用于 MySQL 8.4 LTS 及 9.x 。新部署请改用 InnoDB Cluster;若必须保留传统一主多从,可评估社区版 MHA-Go (见 MySQL 8.4 上使用社区版 MHA-Go )
0.什么是 MHA
MHA(Master High Availability Manager and Tools for MySQL)是一套用 Perl 写的 MySQL 主从高可用工具 ,专门盯主库是否存活;主库挂了之后,在尽量少丢数据的前提下,把最合适的从库提升成新主,并把其它从库改挂到新主上。主从复制本身怎么搭见 MySql单节点、主从、双主的构建方法 ,本文只讲在已有一主多从上套 MHA。
能解决什么问题
普通主从:主库宕机不会自动切 ,要人工改应用连接、改 CHANGE MASTER,期间服务停写。
MHA:Manager 持续探测;判定主库不可用后,自动完成选主、补齐差异日志、切换复制关系,切换过程通常在数十秒内(文中常说约 30 秒量级,实际取决于网络、数据量和是否配 VIP/脚本)。
顺带支持在线主从切换 (按需把当前主换成某个从),不只是故障转移。
架构里有两类角色
角色
装什么
干什么
Manager
mha4mysql-manager
一般单独一台。监控各节点、发起 failover、写管理日志
Node
mha4mysql-node
部署在每一台 MySQL 上。解析 binlog/relay log,配合 Manager 做日志补齐与切换
典型拓扑就是本文后面的规划:1 个 Manager + 1 主 + N 从。Manager 通过 SSH 连各 Node,再连 MySQL;所以免密 SSH、复制账号、管理账号都是后文必配项。
故障转移时大致在干什么
确认主库真挂了(可配二次检查,避免网络抖动误切)。
在从库里选出延迟最小、日志最新 的候选新主。
若旧主还有从库未收到的 binlog,尽量从其它从库或旧主把差异补齐,降低丢数。
提升新主(去掉只读、成为可写源),其余从库 CHANGE MASTER 指向新主并启动复制。
若配置了 master_ip_failover_script,把 VIP 漂到新主,应用仍连虚拟 IP,不用改连接串;未配脚本则要自己改应用指向或用 DNS/代理。
注意:MHA 建立在异步/半同步主从 之上,不是同步多主集群。它尽量保一致、缩短切换时间,但极端情况下仍可能丢尚未传到从库的事务(半同步能缩小窗口,见 MySql单节点、主从、双主的构建方法 )。Manager 本身也是单点,生产上通常再给 Manager 做监控/重启保活。
和本文的关系
下文按顺序做:节点规划 → SSH 免密 → 安装 Manager/Node → 主从基线 → 账号与 mha.cnf → 启动与故障转移演练。先读完这一节,再动手配,不容易把「选主 / 补日志 / VIP」和单纯的主从复制混在一起。
官网与包:
还适用于最新 MySQL 吗
不建议拿这套方案去跑 MySQL 8.4 / 9.x。 本文记录的是 2022 年在 mysql-8.0.30 + mha4mysql-0.58 上验证过的历史做法。
MHA 0.58 发布于 2018 年 3 月,上游最后一次推送大约在 2020 年,之后没有官方新版本。它在 MySQL 8.0 上通常还能跑(社区也有 8.0.16、8.0.27 的部署记录),但原版 无法直接兼容 MySQL 8.4 LTS 及 9.x 。原因是 MHA 源码大量依赖已被删除的复制语句和认证方式:
本文 / MHA 0.58 仍在用的
MySQL 8.4 起
SHOW MASTER STATUS
已删除,改 SHOW BINARY LOG STATUS
SHOW SLAVE STATUS
已删除,改 SHOW REPLICA STATUS
CHANGE MASTER TO
已删除,改 CHANGE REPLICATION SOURCE TO
START SLAVE / STOP SLAVE
已删除,改 START REPLICA / STOP REPLICA
RESET MASTER
已删除,改 RESET BINARY LOGS AND GTIDS
mysql_native_password
8.4 默认关闭,9.0 已删除
简单替换关键字往往不够:MHA 还要解析 SHOW SLAVE STATUS 的列名、拼 CHANGE MASTER TO、靠 SSH + Perl 节点脚本补 binlog。有人试过改源码迁 8.4,结果连从库都认不全。CyberAgent 的实测结论也是:原版 MHA(以及同样停更的 Orchestrator)在 8.4 上跑不起来 。
所以:本文可以当「MySQL 8.0 一主多从怎么套 MHA」的操作笔记;新环境不要再装这套 。已有 8.0 + MHA 的存量系统可以继续维护,但升级到 8.4 之前必须换高可用方案,不能指望 MHA 跟着升。
类似工具怎么选
按「还是不是传统异步主从 + 外部选主」分开看:
方案
适合什么
和 MHA 的关系
现状
MySQL InnoDB Cluster
新部署首选。至少 3 节点,Group Replication + MySQL Shell + MySQL Router
不是「主从外面再挂一个 Manager」,集群自己选主,Router 把写流量指到当前 Primary
官方维护,跟 8.4 LTS
Percona XtraDB Cluster 8.4
要更强一致性的三节点集群
Galera 准同步,不是异步主从;前端再配 ProxySQL / HAProxy
有 8.4 发行版,持续更新
MHA-Go
仍想保持「一主多从 + 外部 failover」、且已上 8.4.x 或 9.7 + GTID
Perl MHA 的 Go 重写:单二进制、不强制每台装 Node
社区项目;不支持 5.7 / 8.0 / 9.6 ,详见下文与 MySQL 8.4 上使用社区版 MHA-Go
Orchestrator
曾经是 MHA 的主流替代(拓扑发现、选主、Web)
同类:管异步复制拓扑
原仓库 2025 年已归档;Percona fork 主要服务其 K8s Operator,不适合当 8.4 新架构的默认选择
云厂商高可用
RDS / Aurora / 云数据库多可用区
平台负责选主和 VIP
能上云就优先用平台能力
社区版 MHA-Go 的版本边界
原版 Perl MHA(本文 0.58)停更后,社区有 Go 重写项目 mha_go 。它不是 原版的补丁分支,而是按现代 MySQL GTID 单主拓扑重写的工具,和本文的 RPM/INI/masterha_manager 不通用 。
按项目 README(写作时以 v0.1.4 为准):
MySQL 版本
MHA-Go
8.4.x
主力支持(发布基线)
9.7 ER/EA
前向兼容目标;配置里 version_series 写 "9.7"
5.7 / 8.0 / 9.6
不支持
非 GTID(文件位点复制)
不支持
因此:
本文的 mysql-8.0.30 + mha4mysql-0.58 不能 换成装一个 MHA-Go 就继续用;8.0 存量若仍要外部 failover,只能继续维护原版 MHA,或先迁到 8.4 + GTID 再评估 MHA-Go。
新环境若坚持「异步一主多从 + 外部选主」,目标应是 MySQL 8.4 LTS + GTID + MHA-Go ,用法见 MySQL 8.4 上使用社区版 MHA-Go 。
更推荐的新架构仍是 InnoDB Cluster (见 MySQL 8.4 InnoDB Cluster 的构建方法 ),不必为了「像以前一样用 MHA」而强行上 MHA-Go。
补充两点,避免和 MHA 的职责搞混:
新部署建议直接:MySQL 8.4 LTS + InnoDB Cluster + MySQL Router 。跨机房容灾再看 InnoDB ClusterSet。只有必须保留传统异步主从、又已经是 8.4 + GTID 时,才考虑评估 MHA-Go;不要在新环境继续装本文的 0.58 RPM。
参考:
1.节点规划
1 2 3 4 mha-manager: 10.250.0.91 MHA控制器,用于监控管理 mysql-master: 10.250.0.118 Mysql主服务器 mysql-slave1: 10.250.0.186 Mysql从服务器 mysql-slave2: 10.250.0.102 Mysql从服务器
1 2 3 4 5 vim /etc/hosts 10.250.0.91 node1 10.250.0.118 node2 10.250.0.186 node3 10.250.0.102 node4
2.免密登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@ip-10-250-0-91 .ssh]# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:C8JdDpbsQP+NeLSPFvHz3BkLigak3fQusApbrPNL9tw root@ip-10-250-0-91.cn-northwest-1.compute.internal The key's randomart image is: +---[RSA 2048]----+ | . | | . o . | | . B = | | . O X B | | + O S = . . | | . . * B = o + | | . = . B + o + | | .B + + . | | oo+.o E | +----[SHA256]-----+
1 2 [root@ip-10-250-0-91 .ssh]# ls authorized_keys id_rsa id_rsa.pub
1 2 3 4 [root@ip-10-250-0-91 .ssh]# ssh-copy-id -i id_rsa.pub root@node1 [root@ip-10-250-0-91 .ssh]# ssh-copy-id -i id_rsa.pub root@node2 [root@ip-10-250-0-91 .ssh]# ssh-copy-id -i id_rsa.pub root@node3 [root@ip-10-250-0-91 .ssh]# ssh-copy-id -i id_rsa.pub root@node4
1 Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
这是由于没有开放密码登录权限导致的,解决方法如下:
1 2 3 4 5 6 7 8 9 passwd root vim /etc/ssh/sshd_config PasswordAuthentication yes systemctl restart sshd.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@ip-10-250-0-91 .ssh]# ssh-copy-id -i id_rsa.pub root@node4 /bin/ssh-copy-id: INFO: Source of key(s) to be installed: "id_rsa.pub" The authenticity of host 'node4 (10.250.0.102)' can't be established. ECDSA key fingerprint is SHA256:aXIII+S5nOVy6pqP1fuaW6fYFsVIN9TBFVP/Xaf8Pds. ECDSA key fingerprint is MD5:1f:07:2f:04:75:77:68:8d:f0:20:96:f1:0b:90:ac:61. Are you sure you want to continue connecting (yes/no)? yes /bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@node4' s password:Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'root@node4'" and check to make sure that only the key(s) you wanted were added.
3.下载及安装MHA安装包
1 2 3 4 5 6 7 8 9 wget https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58-0.el7.centos.noarch.rpm yum install -y mha4mysql-node-0.58-0.el7.centos.noarch.rpm wget https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm yum install -y mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
1 2 3 4 5 6 7 8 错误:软件包:mha4mysql-manager-0.58-0.el7.centos.noarch (/mha4mysql-manager-0.58-0.el7.centos.noarch) 需要:perl(Log::Dispatch::File) 错误:软件包:mha4mysql-manager-0.58-0.el7.centos.noarch (/mha4mysql-manager-0.58-0.el7.centos.noarch) 需要:perl(Parallel::ForkManager) 错误:软件包:mha4mysql-manager-0.58-0.el7.centos.noarch (/mha4mysql-manager-0.58-0.el7.centos.noarch) 需要:perl(Log::Dispatch) 错误:软件包:mha4mysql-manager-0.58-0.el7.centos.noarch (/mha4mysql-manager-0.58-0.el7.centos.noarch) 需要:perl(Log::Dispatch::Screen)
解决方法是先安装epel-release依赖
1 yum install epel-release -y
如果使用aws云服务器则通过下面的命令安装epel
1 sudo amazon-linux-extras install epel -y
4.mysql主从复制搭建
1.所有mysql的server-id不能相同
2.所有mysql打开binlog日志和中继日志,且relay_log_purge=0
3.从节点开启只读,read_only=1
4.云服务通过镜像创建的mysql要注意所有mysql的uuid不能相同。
5.从节点也要创建同步帐号
注意:
这个方式是错误的,会导致主从切换后,slave不能与新的master实现数据同步,
因为此时两个从库的GTID事务和binlog记录的位置已经不一致了。
这个错误的结果在下文"11.测试 MHA 故障转移"有说明。
正确的做法是,将master的数据库导入到两个slave中,主从一旦建立,从库就不要写入任何数据。
1 2 3 4 5 6 7 8 9 mysql> stop slave; mysql> CREATE USER 'vagrant' @'10.250.%.%' IDENTIFIED BY 'vagrant' ; mysql> ALTER USER 'vagrant' @'10.250.%.%' IDENTIFIED WITH mysql_native_password BY 'vagrant' ; mysql> GRANT REPLICATION SLAVE ON *.* TO 'vagrant' @'10.250.%.%' ; mysql> FLUSH PRIVILEGES; mysql> start slave;
正确的做法是,将master的数据库导入到两个slave中,然后再开启主从同步。
注意:主从一旦建立,从库就不要写入任何数据。
1 2 3 4 mysqldump -uroot --all-databases --triggers --routines --events -p -h node2 > all_databases.sql mysql -uroot -p < all_databases.sql
因为开启了GTID,导入数据库时会报如下错误:
1 @@GLOBAL.GTID_PURGED cannot be changed: the added gtid set must not overlap with @@GLOBAL.GTID_EXECUTED
解决方法是在从库中执行如下命令后再导入:
1 2 3 mysql> stop slave; mysql> reset slave all; mysql> reset master;
从节点设置同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 mysql> show master status; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000012 | 197 | | | 6e9a571e-330e-11ed-a3f8-0a53e7cced42:1-9 | +------------------+----------+--------------+------------------+------------------------------------------+ 1 row in set (0.00 sec) mysql> CHANGE MASTER TO MASTER_HOST='10.250.0.118' , MASTER_PORT=3306, MASTER_USER='vagrant' , MASTER_PASSWORD='vagrant' , MASTER_LOG_FILE='mysql-bin.000012' , MASTER_LOG_POS=197; mysql> START slave; mysql> SHOW slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for source to send event Master_Host: 10.250.0.118 Master_User: vagrant Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000012 Read_Master_Log_Pos: 197 Relay_Log_File: slave-relay-bin.000002 Relay_Log_Pos: 326 Relay_Master_Log_File: mysql-bin.000012 Slave_IO_Running: Yes Slave_SQL_Running: Yes
6.MHA需要一个拥有管理员权限的mysql用户
1 2 3 4 mysql> CREATE USER 'mhaadmin' @'10.250.%.%' IDENTIFIED BY 'mhapass' ; mysql> ALTER USER 'mhaadmin' @'10.250.%.%' IDENTIFIED WITH mysql_native_password BY 'mhapass' ; mysql> GRANT ALL ON *.* TO 'mhaadmin' @'10.250.%.%' ; mysql> FLUSH PRIVILEGES;
7.在mha-manager节点上编写MHA管理配置文件
1 2 mkdir /etc/mha_mastervim /etc/mha_master/mha.cnf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 [server default] user=mhaadmin password=mhapass port=3306 manager_workdir=/etc/mha_master manager_log=/etc/mha_master/manager.log remote_workdir=/mydata/mha_master ssh_user=root repl_user=vagrant repl_password=vagrant ping_interval=1 [server1] hostname=10.250.0.118 ssh_port=22 candidate_master=1 [server2] hostname=10.250.0.186 ssh_port=22 candidate_master=1 [server3] hostname=10.250.0.102 ssh_port=22 candidate_master=1
注意,所有mysql节点都需要创建该目录/mydata/mha_master
8.检测各节点间ssh互信通信配置是否正确
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@ip-10-250-0-91 mha_master]# masterha_check_ssh --conf=/etc/mha_master/mha.cnf Thu Sep 15 10:13:08 2022 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping. Thu Sep 15 10:13:08 2022 - [info] Reading application default configuration from /etc/mha_master/mha.cnf.. Thu Sep 15 10:13:08 2022 - [info] Reading server configuration from /etc/mha_master/mha.cnf.. Thu Sep 15 10:13:08 2022 - [info] Starting SSH connection tests.. Thu Sep 15 10:13:09 2022 - [debug] Thu Sep 15 10:13:08 2022 - [debug] Connecting via SSH from root@10.250.0.118(10.250.0.118:22) to root@10.250.0.186(10.250.0.186:22).. Thu Sep 15 10:13:08 2022 - [debug] ok. Thu Sep 15 10:13:08 2022 - [debug] Connecting via SSH from root@10.250.0.118(10.250.0.118:22) to root@10.250.0.102(10.250.0.102:22).. Thu Sep 15 10:13:08 2022 - [debug] ok. Thu Sep 15 10:13:09 2022 - [debug] Thu Sep 15 10:13:08 2022 - [debug] Connecting via SSH from root@10.250.0.186(10.250.0.186:22) to root@10.250.0.118(10.250.0.118:22).. Thu Sep 15 10:13:08 2022 - [debug] ok. Thu Sep 15 10:13:08 2022 - [debug] Connecting via SSH from root@10.250.0.186(10.250.0.186:22) to root@10.250.0.102(10.250.0.102:22).. Thu Sep 15 10:13:09 2022 - [debug] ok. Thu Sep 15 10:13:10 2022 - [debug] Thu Sep 15 10:13:09 2022 - [debug] Connecting via SSH from root@10.250.0.102(10.250.0.102:22) to root@10.250.0.118(10.250.0.118:22).. Thu Sep 15 10:13:09 2022 - [debug] ok. Thu Sep 15 10:13:09 2022 - [debug] Connecting via SSH from root@10.250.0.102(10.250.0.102:22) to root@10.250.0.186(10.250.0.186:22).. Thu Sep 15 10:13:09 2022 - [debug] ok. Thu Sep 15 10:13:10 2022 - [info] All SSH connection tests passed successfully.
看到All SSH connection tests passed successfully.说明通信成功
9.检查管理的MySQL复制集群的连接配置参数是否OK
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [root@ip-10-250-0-91 mha_master]# masterha_check_repl --conf=/etc/mha_master/mha.cnf Thu Sep 15 10:16:35 2022 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping. Thu Sep 15 10:16:35 2022 - [info] Reading application default configuration from /etc/mha_master/mha.cnf.. Thu Sep 15 10:16:35 2022 - [info] Reading server configuration from /etc/mha_master/mha.cnf.. Thu Sep 15 10:16:35 2022 - [info] MHA::MasterMonitor version 0.58. Thu Sep 15 10:16:36 2022 - [info] GTID failover mode = 1 Thu Sep 15 10:16:36 2022 - [info] Dead Servers: Thu Sep 15 10:16:36 2022 - [info] Alive Servers: Thu Sep 15 10:16:36 2022 - [info] 10.250.0.118(10.250.0.118:3306) Thu Sep 15 10:16:36 2022 - [info] 10.250.0.186(10.250.0.186:3306) Thu Sep 15 10:16:36 2022 - [info] 10.250.0.102(10.250.0.102:3306) Thu Sep 15 10:16:36 2022 - [info] Alive Slaves: Thu Sep 15 10:16:36 2022 - [info] 10.250.0.186(10.250.0.186:3306) Version=8.0.30 (oldest major version between slaves) log-bin:enabled Thu Sep 15 10:16:36 2022 - [info] GTID ON Thu Sep 15 10:16:36 2022 - [info] Replicating from 10.250.0.118(10.250.0.118:3306) Thu Sep 15 10:16:36 2022 - [info] Primary candidate for the new Master (candidate_master is set ) Thu Sep 15 10:16:36 2022 - [info] 10.250.0.102(10.250.0.102:3306) Version=8.0.30 (oldest major version between slaves) log-bin:enabled Thu Sep 15 10:16:36 2022 - [info] GTID ON Thu Sep 15 10:16:36 2022 - [info] Replicating from 10.250.0.118(10.250.0.118:3306) Thu Sep 15 10:16:36 2022 - [info] Primary candidate for the new Master (candidate_master is set ) Thu Sep 15 10:16:36 2022 - [info] Current Alive Master: 10.250.0.118(10.250.0.118:3306) Thu Sep 15 10:16:36 2022 - [info] Checking slave configurations.. Thu Sep 15 10:16:36 2022 - [info] Checking replication filtering settings.. Thu Sep 15 10:16:36 2022 - [info] binlog_do_db= , binlog_ignore_db= Thu Sep 15 10:16:36 2022 - [info] Replication filtering check ok. Thu Sep 15 10:16:36 2022 - [info] GTID (with auto-pos) is supported. Skipping all SSH and Node package checking. Thu Sep 15 10:16:36 2022 - [info] Checking SSH publickey authentication settings on the current master.. Thu Sep 15 10:16:36 2022 - [info] HealthCheck: SSH to 10.250.0.118 is reachable. Thu Sep 15 10:16:36 2022 - [info] 10.250.0.118(10.250.0.118:3306) (current master) +--10.250.0.186(10.250.0.186:3306) +--10.250.0.102(10.250.0.102:3306) Thu Sep 15 10:16:36 2022 - [info] Checking replication health on 10.250.0.186.. Thu Sep 15 10:16:36 2022 - [info] ok. Thu Sep 15 10:16:36 2022 - [info] Checking replication health on 10.250.0.102.. Thu Sep 15 10:16:36 2022 - [info] ok. Thu Sep 15 10:16:36 2022 - [warning] master_ip_failover_script is not defined. Thu Sep 15 10:16:36 2022 - [warning] shutdown_script is not defined. Thu Sep 15 10:16:36 2022 - [info] Got exit code 0 (Not master dead). MySQL Replication Health is OK.
看到MySQL Replication Health is OK.说明成功
10.管理MHA
1 2 3 4 [root@ip-10-250-0-91 mha_master]# rm -rf /etc/mha_master/mha.failover.complete [root@ip-10-250-0-91 mha_master]# nohup masterha_manager --conf=/etc/mha_master/mha.cnf &> /etc/mha_master/manager.log &
查看master的状态
1 2 3 [root@ip-10-250-0-91 mha_master]# masterha_check_status --conf=/etc/mha_master/mha.cnf mha (pid:9696) is running(0:PING_OK), master:10.250.0.118
上面的信息中“mha (pid:9696) is running(0:PING_OK)”表示MHA服务运行 OK,否则, 则会显示为类似“mha is stopped(1:NOT_RUNNING).”
停止MHA
1 2 3 4 [root@ip-10-250-0-91 mha_master]# masterha_stop --conf=/etc/mha_master/mha.cnf Stopped mha successfully. [1]+ 退出 1 nohup masterha_manager --conf=/etc/mha_master/mha.cnf &>/etc/mha_master/manager.log
11.测试 MHA 故障转移
我们先按"5.从节点也要创建同步帐号"中的错误方式进行测试
启动MHA
关闭mysql-master数据库服务
监控mha-manager的日志
1 2 3 4 5 6 7 [root@ip-10-250-0-91 mha_master]# tail -f manager.log ……………………………… Started automated(non-interactive) failover. Selected 10.250.0.186(10.250.0.186:3306) as a new master. 10.250.0.186(10.250.0.186:3306): OK: Applying all logs succeeded. 10.250.0.102(10.250.0.102:3306): ERROR: Failed on waiting gtid exec set on master. Master failover to 10.250.0.186(10.250.0.186:3306) done , but recovery on slave partially failed.
可以看到mha监测到master挂掉后,会进行重新选主,并且选主成功,但是为其挂载从库失败。
我们来分析一下这个失败的原因
1 2 3 4 5 6 7 8 mysql> show master status\G; *************************** 1. row *************************** File: mysql-bin.000013 Position: 2466 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 6e9a571e-330e-11ed-a3f8-0a53e7cced42:5-9, 6e9a571e-330e-11ed-a3f8-0a53e7cced43:1-4
1 2 3 4 5 6 7 8 9 mysql> show variables like "%read_only%" ; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_read_only | OFF | | read_only | OFF | | super_read_only | OFF | | transaction_read_only | OFF | +-----------------------+-------+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 mysql> show slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for source to send event Master_Host: 10.250.0.186 Master_User: vagrant Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000013 Read_Master_Log_Pos: 2466 Relay_Log_File: slave-relay-bin.000002 Relay_Log_Pos: 420 Relay_Master_Log_File: mysql-bin.000013 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1396 Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 1 failed executing transaction '6e9a571e-330e-11ed-a3f8-0a53e7cced43:1' at master log mysql-bin.000013, end_log_pos 1465. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
1 2 3 4 5 6 7 8 9 mysql> select * from performance_schema.replication_applier_status_by_worker\G; *************************** 1. row *************************** CHANNEL_NAME: WORKER_ID: 1 THREAD_ID: NULL SERVICE_STATE: OFF LAST_ERROR_NUMBER: 1396 LAST_ERROR_MESSAGE: Worker 1 failed executing transaction '6e9a571e-330e-11ed-a3f8-0a53e7cced43:1' at master log mysql-bin.000013, end_log_pos 1465; Error 'Operation CREATE USER failed for ' vagrant'@' 10.250.%.%'' on query. Default database: 'mysql' . Query: 'CREATE USER ' vagrant'@' 10.250.%.%' IDENTIFIED WITH ' mysql_native_password' AS ' *04E6E1273D1783DF7D57DC5479FE01CFFDFD0058'' …………
这里提示的比较清楚,意思是从库在同步新的主库数据时,执行创建同步帐号的CREATE USER语句时报错
思考我们的构建过程,从库创建同步帐号是分别创建在自己的数据库里的,所以两个从库GTID事务和binlog日志实际上数据并不一致,导致主从切换后,从库要从新的master同步数据时,就会同步这个创建帐号的语句,但是此时从库已经有这个帐号了,所以创建帐号失败,从而导致主从复制失败。
此时的解决方法就是将master的数据库全量导入到slave中,然后重新建立主从,参照下文。
我们再按"5.从节点也要创建同步帐号"中的正确方式搭建好主从后进行测试
1 2 3 4 5 6 7 ………… Started automated(non-interactive) failover. Selected 10.250.0.186(10.250.0.186:3306) as a new master. 10.250.0.186(10.250.0.186:3306): OK: Applying all logs succeeded. 10.250.0.102(10.250.0.102:3306): OK: Slave started, replicating from 10.250.0.186(10.250.0.186:3306) 10.250.0.186(10.250.0.186:3306): Resetting slave info succeeded. Master failover to 10.250.0.186(10.250.0.186:3306) completed successfully.
此时说明新的master切换成功,并且成功建立了主从。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 mysql> show master status; +------------------+----------+--------------+------------------+------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+------------------------------------------+ | mysql-bin.000001 | 157 | | | 6e9a571e-330e-11ed-a3f8-0a53e7cced42:1-9 | +------------------+----------+--------------+------------------+------------------------------------------+ mysql> show slave status \G; *************************** 1. row *************************** Slave_IO_State: Waiting for source to send event Master_Host: 10.250.0.186 Master_User: vagrant Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 157 Relay_Log_File: slave-relay-bin.000002 Relay_Log_Pos: 373 Relay_Master_Log_File: mysql-bin.000001 Slave_IO_Running: Yes Slave_SQL_Running: Yes
此时说明MHA搭建及测试成功。
知识点:关于MHA有几点内容需要说明:
1.MHA监控主从一旦完成切换,MHA服务进程就会停止,需要mysql集群修复完成后手工重新启动
2.挂掉的mysql完成修复后可以作为slave加入mysql主从集群,注意修改其mysql配置文件,加入时必须重新全量导入master的数据库
3.新加入mysql时要注意ip是否发生变更,如果变更要及时修改MHA的配置文件
4.MHA切换主从后,新的master配置文件中的read_only不会被修改,需要手工修改,否则重启服务器后不能写入数据
5.MHA只会监控master,slave挂掉时不会触发MHA做任何操作,此时只要从库的ip没有变化,修复后可以直接启动即可。
6.故障转移发生后,MHA工作目录会生成mha.failover.complete文件,如果要在故障发生后8小时内重新启动MHA,则重新启动MHA前一定要查看工作目录下是否存在mha.failover.complete文件,如果存在要先删除,否则不能完成新一轮的主从切换,或者在启动MHA时加上--ignore_last_failover。
新的问题:
1.MHA可以帮助我们实现master的监控,当其监测到master不可用时会在slave中进行选主,从而实现主从切换。
2.但是这里有个问题,主从切换后,新的master的ip就会发生变化,所以客户端连接时需要改变为新的ip地址。
3.那有什么办法可以在不改变客户端连接mysql的地址情况下,自动完成切换呢,答案就是VIP(虚拟IP).
MHA脚本扩展
1 2 3 4 5 6 7 8 9 10 11 [root@ip-10-250-0-91 mha_master]# masterha_check_repl --conf=/etc/mha_master/mha.cnf ……………… Thu Sep 15 10:16:36 2022 - [info] Checking replication health on 10.250.0.186.. Thu Sep 15 10:16:36 2022 - [info] ok. Thu Sep 15 10:16:36 2022 - [info] Checking replication health on 10.250.0.102.. Thu Sep 15 10:16:36 2022 - [info] ok. Thu Sep 15 10:16:36 2022 - [warning] master_ip_failover_script is not defined. Thu Sep 15 10:16:36 2022 - [warning] shutdown_script is not defined. Thu Sep 15 10:16:36 2022 - [info] Got exit code 0 (Not master dead). MySQL Replication Health is OK.
这里有两个警告,master_ip_failover_script is not defined.和shutdown_script is not defined.
shutdown_script脚本用于指定故障发生后关闭故障主机的脚本,而master_ip_failover_script脚本用于指定故障转移时需要做的操作。
实际上,MHA支持多种脚本扩展
1 2 3 4 5 6 secondary_check_script:用于检查来自多个网络路由的master可用性 master_ip_failover_script:用于更新应用程序使用的master的ip地址 shutdown_script:为了强制关机master report_script:用于发送报告 init_conf_load_script:用于加载初始配置参数 master_ip_online_change_script:用于更新masterIP地址。这不用于主故障转移,而是用于在线主交换机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 use strict;use warnings FATAL => 'all' ;use Getopt::Long;my ( $command , $orig_master_host , $orig_master_ip ,$ssh_user , $orig_master_port , $new_master_host , $new_master_ip ,$new_master_port , $orig_master_ssh_port ,$new_master_ssh_port ,$new_master_user ,$new_master_password ); my $vip = '10.250.0.199' ;my $key = '1' ;my $ssh_start_vip = "sudo /sbin/ifconfig eth0:$key $vip up" ;my $ssh_stop_vip = "sudo /sbin/ifconfig eth0:$key down" ;my $ssh_Bcast_arp = "sudo /sbin/arping -I eth0 -c 3 -A $vip " ;GetOptions( 'command=s' => \$command , 'ssh_user=s' => \$ssh_user , 'orig_master_host=s' => \$orig_master_host , 'orig_master_ip=s' => \$orig_master_ip , 'orig_master_port=i' => \$orig_master_port , 'orig_master_ssh_port=i' => \$orig_master_ssh_port , 'new_master_host=s' => \$new_master_host , 'new_master_ip=s' => \$new_master_ip , 'new_master_port=i' => \$new_master_port , 'new_master_ssh_port' => \$new_master_ssh_port , 'new_master_user' => \$new_master_user , 'new_master_password' => \$new_master_password ); exit &main();sub main { $ssh_user = defined $ssh_user ? $ssh_user : 'root' ; print "\n\n SCRIPT START \[$ssh_user |$ssh_start_vip \]" ; print "\n SCRIPT STOP \[$ssh_user |$ssh_stop_vip \]\n\n" ; if ( $command eq "stop" || $command eq "stopssh" ) { my $exit_code = 1 ; eval { print "Disabling the VIP on old master: $orig_master_host \n" ; &stop_vip(); $exit_code = 0 ; }; if ($@ ) { warn "Got Error: $@ \n" ; exit $exit_code ; } exit $exit_code ; } elsif ( $command eq "start" ) { my $exit_code = 10 ; eval { print "Enabling the VIP - $vip on the new master - $new_master_host \n" ; &start_vip(); &start_arp(); $exit_code = 0 ; }; if ($@ ) { warn $@ ; exit $exit_code ; } exit $exit_code ; } elsif ( $command eq "status" ) { print "Checking the Status of the script.. OK \n" ; exit 0 ; } else { &usage(); exit 1 ; } } sub start_vip () { `ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"` ; } sub stop_vip () { `ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"` ; } sub start_arp () { `ssh $ssh_user\@$new_master_host \" $ssh_Bcast_arp \"` ; } sub usage { print "Usage: master_ip_failover --command=start|stop|stopssh|status --ssh_user=user --orig_master_host=host --orig_master_ip=ip --orig_master_port=port --new_master_host=host --new_master_ip=ip --new_master_port=port\n" ; }
1 2 3 4 5 6 7 ………… ping_interval=1 master_ip_failover_script=/etc/mha_master/master_ip_failover_script …………
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@ip-10-250-0-118 mha_master]# ifconfig eth0:1 10.250.0.199/24 [root@ip-10-250-0-118 mha_master]# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 10.250.0.118 netmask 255.255.255.0 broadcast 10.250.0.255 inet6 fe80::803:41ff:fe19:356a prefixlen 64 scopeid 0x20<link > ether 0a:03:41:19:35:6a txqueuelen 1000 (Ethernet) RX packets 12260 bytes 2511078 (2.3 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10202 bytes 5216046 (4.9 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 10.250.0.199 netmask 255.255.255.0 broadcast 10.250.0.255 ether 0a:03:41:19:35:6a txqueuelen 1000 (Ethernet) lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 43 bytes 9386 (9.1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 43 bytes 9386 (9.1 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@ip-10-250-0-91 mha_master]# masterha_check_repl --conf=/etc/mha_master/mha.cnf ……………… Fri Sep 16 07:06:02 2022 - [info] Checking replication health on 10.250.0.186.. Fri Sep 16 07:06:02 2022 - [info] ok. Fri Sep 16 07:06:02 2022 - [info] Checking replication health on 10.250.0.102.. Fri Sep 16 07:06:02 2022 - [info] ok. Fri Sep 16 07:06:02 2022 - [info] Checking master_ip_failover_script status: Fri Sep 16 07:06:02 2022 - [info] /etc/mha_master/master_ip_failover_script --command =status --ssh_user=root --orig_master_host=10.250.0.118 --orig_master_ip=10.250.0.118 --orig_master_port=3306 SCRIPT START [root|sudo /sbin/ifconfig eth0:1 10.250.0.199 up] SCRIPT STOP [root|sudo /sbin/ifconfig eth0:1 down] Checking the Status of the script.. OK Fri Sep 16 07:06:02 2022 - [info] OK. Fri Sep 16 07:06:02 2022 - [warning] shutdown_script is not defined. Fri Sep 16 07:06:02 2022 - [info] Got exit code 0 (Not master dead). MySQL Replication Health is OK.
重新启动MHA
停止mysql-master服务
查看MHA日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ----- Failover Report ----- mha: MySQL Master failover 10.250.0.118(10.250.0.118:3306) to 10.250.0.186(10.250.0.186:3306) succeeded Master 10.250.0.118(10.250.0.118:3306) is down! Check MHA Manager logs at ip-10-250-0-91.cn-northwest-1.compute.internal:/etc/mha_master/manager.log for details. Started automated(non-interactive) failover. Invalidated master IP address on 10.250.0.118(10.250.0.118:3306) Selected 10.250.0.186(10.250.0.186:3306) as a new master. 10.250.0.186(10.250.0.186:3306): OK: Applying all logs succeeded. 10.250.0.186(10.250.0.186:3306): OK: Activated master IP address. 10.250.0.102(10.250.0.102:3306): OK: Slave started, replicating from 10.250.0.186(10.250.0.186:3306) 10.250.0.186(10.250.0.186:3306): Resetting slave info succeeded. Master failover to 10.250.0.186(10.250.0.186:3306) completed successfully.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@ip-10-250-0-186 mysql8]# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 10.250.0.186 netmask 255.255.255.0 broadcast 10.250.0.255 inet6 fe80::895:fcff:fe7f:ee0c prefixlen 64 scopeid 0x20<link > ether 0a:95:fc :7f:ee:0c txqueuelen 1000 (Ethernet) RX packets 12284 bytes 3862460 (3.6 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10558 bytes 2559446 (2.4 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 10.250.0.199 netmask 255.0.0.0 broadcast 10.255.255.255 ether 0a:95:fc :7f:ee:0c txqueuelen 1000 (Ethernet) lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 41 bytes 9270 (9.0 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 41 bytes 9270 (9.0 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
另外查询原master,其也已经解除了虚拟IP的绑定,此时说明虚拟IP迁移成功,故障转移成功!
AWS不支持在EC2上开启VIP,需要使用其它解决方案,这个不在这里讨论。
可以在MHA配置文件中配置report_script,其对应一个脚本文件,用于故障转移时发出告警信息。
可以发送邮件,也可以调用企业微信或者钉钉的接口实现,这里不再赘述。