MySQL高可用(MHA)

什么是MHA

MHA(Master High Availability)是一套针对MySQL的高可用解决方案,能实现自动故障检测与主从切换,有效减少MySQL故障恢复时间,提高服务可用性。

其核心特点包括:

  1. 自动故障检测与切换:通过定期监控主库状态,当主库出现故障(如宕机、网络中断等)时,能自动识别并在从库中选择最合适的节点提升为新主库,整个过程无需人工干预。

  2. 数据一致性保障:在切换过程中,会尽量复制主库未同步到从库的binlog日志,最大程度减少数据丢失;支持GTID(全局事务标识),简化binlog定位与同步流程。

  3. 灵活的候选主库选择:可通过配置指定优先成为新主库的候选节点,优先选择数据最新、负载较低的从库,确保切换后服务稳定。

  4. 低性能损耗:监控过程对MySQL集群性能影响极小,仅通过SSH和MySQL账号进行轻量通信。

  5. 架构简洁:由Manager(管理节点)和Node(数据节点代理)两部分组成,Manager负责监控和决策,Node部署在所有MySQL节点上执行具体操作(如复制管理、binlog处理等)。

MHA适用于一主多从的MySQL架构,广泛应用于需要高可用保障的生产环境,尤其适合对数据库 downtime 敏感的业务场景。不过需要注意的是,它本身不提供负载均衡功能,通常需与其他工具(如ProxySQL、HAProxy)配合使用以实现读写分离和流量切换。

MHA实现原理

(1)从宕机崩溃的master保存二进制日志事件(binlog events);

(2)识别含有最新更新的slave;

(3)应用差异的中继日志(relay log)到其他的slave;

(4)应用从master保存的二进制日志事件(binlog events);

(5)提升一个slave为新的master;

(6)使其他的slave连接新的master进行复制;

MHA搭建流程

要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台master,一台充当备用master,另外一台充当从库。

MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master,整个故障转移过程对应用程序完全透明。

环境准备

服务器规划(至少3节点)

角色

主机名

IP地址

说明

Master(主)

mha-node1

192.168.7.50

主库

Slave1(从1)

mha-node2

192.168.7.51

从库(候选主库)

Slave2(从2)

mha-node3

192.168.7.52

从库

MHA管理节点(独立服务器)实验时根据自身条件可选择性添加

Manager

mha-manager

192.168.7.53

MHA管理节点(独立服务器)

注意

场景

Manager 安装位置

说明

有独立 Manager

单独一台机器(如 mha-manager)

Manager 和 DB 服务分离,互不影响

没有独立 Manager

安装在某一台 DB 节点上(建议装在从库,如 mha-node2)

Manager 和 DB 共用一台机器

建议:如果没有独立机器,把 Manager 装在从库上(而不是主库),这样主库宕机时 Manager 还能正常工作,不会单点失效。

基础配置(所有节点)3台及4台操作方式

 # 关闭防火墙
 systemctl stop firewalld && systemctl disable firewalld
 ​
 # 关闭SELinux
 setenforce 0
 sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
 ​
 # hosts 配置(所有 3 台机器)
 cat >> /etc/hosts << EOF
 192.168.7.50 mha-node1
 192.168.7.51 mha-node2
 192.168.7.52 mha-node3
 EOF
 ​
 # 免密登录(所有 3 台机器分别执行)
 ## 先在当前机器上生成密钥对
 ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
 ​
 ## 再执行循环复制公钥的命令
 for host in mha-node1 mha-node2 mha-node3; do
   ssh-copy-id -i ~/.ssh/id_rsa.pub $host
 done
 ​
 ## 执行过程中,每台机器会依次提示你输入一次密码
 输入正确的 root 密码后,该节点的免密就配置好了,然后会自动继续下一个节点。
 ​
 ​
 # hosts 配置(所有 4 台机器)
 cat >> /etc/hosts << EOF
 192.168.7.50 mha-manager
 192.168.7.51 mha-node1
 192.168.7.52 mha-node2
 192.168.7.53 mha-node3
 EOF
 ​
 # 免密登录(所有 4 台机器分别执行)
 ## 先在当前机器上生成密钥对
 ssh-keygen -t rsa -N '' -f ~/.ssh/id_rsa
 ​
 ## 再执行循环复制公钥的命令
 for host in mha-manager mha-node1 mha-node2 mha-node3; do
   ssh-copy-id -i ~/.ssh/id_rsa.pub $host
 done
 ​
 ## 执行过程中,每台机器会依次提示你输入一次密码
 输入正确的 root 密码后,该节点的免密就配置好了,然后会自动继续下一个节点。
 ​
 ​
 # 操作步骤的区别
 有独立 MHA Manager(4 台机器)
 需要在 4 台机器上分别执行:
 ​
 机器  执行内容
 mha-manager 生成密钥 → 复制公钥到 mha-node1/2/3
 mha-node1   生成密钥 → 复制公钥到 mha-node1/2/3(含自己)
 mha-node2   生成密钥 → 复制公钥到 mha-node1/2/3(含自己)
 mha-node3   生成密钥 → 复制公钥到 mha-node1/2/3(含自己)
 ​
 没有独立 MHA Manager(3 台机器)
 只需在 3 台机器上执行:
 ​
 机器  执行内容
 mha-node1   生成密钥 → 复制公钥到 mha-node1/2/3(含自己)
 mha-node2   生成密钥 → 复制公钥到 mha-node1/2/3(含自己)
 mha-node3   生成密钥 → 复制公钥到 mha-node1/2/3(含自己)
 ​

部署MySQL 8.0主从复制

安装MySQL 8.0(所有数据库节点:mha-node1、mha-node2、mha-node3)

安装方式:脚本安装、yum安装、二进制安装等。

根据个人习惯进行安装、或者要求安装;

配置MySQL(主从差异化配置)

Master(mha-node1)配置

 # 修改配置文件
 vim  /etc/my.cnf
 [mysqld]
 # ...
 # 在默认配置下方添加:
 server-id=50                       # 唯一ID
 log_bin=mysql-bin                  # 开启binlog
 binlog_format=ROW                  # ROW模式(MHA推荐)
 gtid_mode=ON                       # 开启GTID
 enforce_gtid_consistency=ON        # 强制GTID一致性
 log_slave_updates=ON               # 从库同步时记录binlog(用于级联复制)
 skip_name_resolve=ON               # 跳过域名解析
 ​
 # 重启mysql
 systemctl restart mysqld

Slave(mha-node2/mha-node3)配置

 # 修改配置文件
 vim  /etc/my.cnf
 [mysqld]
 # ...
 server-id=51                   # 唯一ID (mha-node3设为52)
 log_bin=mysql-bin
 binlog_format=ROW
 gtid_mode=ON
 enforce_gtid_consistency=ON
 log_slave_updates=ON
 skip_name_resolve=ON
 relay_log=relay-bin            # 开启中继日志
 read_only=ON                   # 从库只读(可选)
 ​
 # 重启mysql
 systemctl restart mysqld

搭建主从复制(基于GTID)

在Master(mha-node1)创建复制用户
 -- MySQL 8.0默认认证插件为caching_sha2_password,MHA需用mysql_native_password
 -- 登录Master的MySQL
 ​
 # 创建复制用户
 CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
 ​
 # 给用户授予权限
 GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
 ​
 # 刷新权限
 FLUSH PRIVILEGES;
在Slave(mha-node2/mha-node3)配置复制
 -- 登录Slave的MySQL
 CHANGE MASTER TO
   MASTER_HOST='mha-node1',
   MASTER_USER='repl',
   MASTER_PASSWORD='123456',
   MASTER_PORT=3306,
   MASTER_AUTO_POSITION=1;  # 基于GTID自动定位
 ​
 -- 启动复制
 START SLAVE;
 ​
 -- 检查复制状态(确保Slave_IO_Running和Slave_SQL_Running均为Yes)
 SHOW SLAVE STATUS\G;

部署MHA

完整安装清单

机器

角色

需要装 Perl 依赖

需要装 MHA Node

需要装 MHA Manager

需要装邮件模块

mha-node1

主库

✅ 需要

✅ 需要

❌ 不需要

❌ 不需要

mha-node2

从库 + Manager

✅ 需要

✅ 需要

✅ 需要

✅ 需要(如要邮件告警)

mha-node3

从库

✅ 需要

✅ 需要

❌ 不需要

❌ 不需要

1. 安装依赖(所有节点)

CentOS7安装依赖

 # 配置yum源确保下载依赖无问题
 ## 配置Yum扩展源
 wget -O /etc/yum.repos.d/epel.repo https://mirrors.aliyun.com/repo/epel-7.repo
 ​
 # 安装Perl依赖(MHA基于Perl开发)
 yum install -y perl-DBD-MySQL perl-CPAN perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker
 ​
 # Perl 模块构建工具
 yum install -y perl-Module-Install
 ​
 # 如需邮件告警(实验环境强烈不推荐安装)(安装位置确定有MHA Manager)
 # 1. 先安装 perl-CPAN(麒麟系统)
 yum install -y perl-CPAN
 ​
 # 2. 用 cpan 安装邮件模块
 cpan -i Email::Sender Email::Valid Mail::Sender

Ubuntu22.04安装依赖包

 sudo apt install -y perl libdbi-perl libdbd-mysql-perl libperl-dev libconfig-tiny-perl liblog-dispatch-perl 

安装MHA包

下载MHA源码包

 # github下载(经常连接不上)
 # 管理节点下载(只在 Manager 节点(mha-node2)下载)
 wget https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-manager-0.58.tar.gz
 ​
 # 所有节点下载(所有 3/4 台机器都要下载)
 wget https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58.tar.gz
 ​
 ​
 # 备选方案(通过代理下载)
 # 管理节点下载(只在 Manager 节点(mha-node2)下载)
 wget https://ghproxy.net/https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-manager-0.58.tar.gz
 ​
 # 所有节点下载(所有 3/4 台机器都要下载)
 wget https://ghproxy.net/https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58.tar.gz

安装MHA Node(所有节点)

 # 1. 解压
 tar -zxvf mha4mysql-node-0.58.tar.gz
 ​
 # 2. 进入目录
 cd mha4mysql-node-0.58
 ​
 # 3. 生成编译配置
 perl Makefile.PL
 ​
 # 4. 编译及安装
 make && make install
 ​

安装MHA Manager(仅manager节点)

 # 1. 解压 Manager 源码包
 tar -zxvf mha4mysql-manager-0.58.tar.gz
 ​
 # 2. 进入解压后的目录
 cd mha4mysql-manager-0.58
 ​
 # 3. 生成编译配置文件
 perl Makefile.PL
 ​
 # 4. 编译及安装
 make && make install
 ​

manager组件安装后在/usr/local/bin下面会生成几个工具,主要包括以下几个:

  • masterha_check_ssh 检查 MHA 的 SSH 配置状况

  • masterha_check_repl 检查 MySQL 复制状况

  • masterha_manger 启动 manager的脚本

  • masterha_check_status 检测当前 MHA 运行状态

  • masterha_master_monitor 检测 master 是否宕机

  • masterha_master_switch 控制故障转移(自动或者手动)

  • masterha_conf_host 添加或删除配置的 server 信息

  • masterha_stop 关闭manager

node组件安装后也会在/usr/local/bin 下面会生成几个脚本(这些工具通常由 MHAManager 的脚本触发,无需人为操作)主要如下:

  • save_binary_logs 保存和复制 master 的二进制日志

  • apply_diff_relay_logs 识别差异的中继日志事件并将其差异的事件应用于其他的 slave

  • filter_mysqlbinlog 去除不必要的 ROLLBACK 事件

  • purge_relay_logs 清除中继日志(Relay-log)

配置MHA

创建MHA配置文件(manager节点)

 # manager节点 (mha-node2)
 # 创建目录
 mkdir -p /etc/mha
 ​
 # 写入配置文件
 cat > /etc/mha/mha_cluster.cnf << EOF
 [server default]
 # MHA管理用户(需在所有MySQL节点创建)
 user=mha_user
 password=123456
 # SSH登录用户
 ssh_user=root
 # MySQL复制用户
 repl_user=repl
 repl_password=123456
 # 健康检查间隔(秒)
 ping_interval=1
 # master的binlog目录
 master_binlog_dir=/usr/local/mysql/data
 # 远程节点临时目录
 remote_workdir=/tmp
 # 二次检查节点(根据Manager安装调整检查节点)
 secondary_check_script=masterha_secondary_check -s mha-node1 -s mha-node3
 # manager工作目录
 manager_workdir=/var/log/mha/mha_cluster
 # manager日志
 manager_log=/var/log/mha/mha_cluster/manager.log
 ​
 [server1]
 hostname=mha-node1
 port=3306
 # 不优先作为候选主库
 candidate_master=0
 ​
 [server2]
 hostname=mha-node2
 port=3306
 # 优先作为候选主库(数据最新时)
 candidate_master=1
 # 忽略复制延迟,强制作为候选
 check_repl_delay=0
 ​
 [server3]
 hostname=mha-node3
 port=3306
 candidate_master=0
 EOF

创建MHA管理用户(主MySQL节点从的会自动同步)

 # 创建用户
 CREATE USER 'mha_user'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
 ​
 # 权限设置
 GRANT ALL PRIVILEGES ON *.* TO 'mha_user'@'%';
 ​
 # 刷新配置
 FLUSH PRIVILEGES;

验证MHA配置

1. 检查SSH连接(manager节点)

 masterha_check_ssh --conf=/etc/mha/mha_cluster.cnf
 # 输出"All SSH connection tests passed successfully."即为正常

2. 检查主从复制(manager节点)

 masterha_check_repl --conf=/etc/mha/mha_cluster.cnf
 # 输出"MySQL Replication Health is OK."即为正常
 # 基本会报错所以需要去修复一下问题
 # 原因是MHA 的代码与系统上高版本的 Perl 不完全兼容。

修复 Perl 语法错误

编辑NodeUtil.pm

 # 编辑配置文件
 vim /usr/local/share/perl5/MHA/NodeUtil.pm
 ​
 # 修改第 195 行
 修改前:
 my $result = sprintf( '%03d%03d%03d', $str =~ m/(\d+)/g );
 ​
 修改后:
 my @nums = ($str =~ m/(\d+)/g);
 my $result = sprintf( '%03d%03d%03d', $nums[0], $nums[1], $nums[2] );
 ​
 # 修改第 201 行
 修改前:
 my $result = sprintf( '%03d%03d', $str =~ m/(\d+)/g );
 ​
 修改后:
 my @nums = ($str =~ m/(\d+)/g);
 my $result = sprintf( '%03d%03d', $nums[0], $nums[1] );

启动MHA Manager

 # 前台启动(测试用,日志实时输出)
 masterha_manager --conf=/etc/mha/mha_cluster.cnf
 ​
 # 后台启动(生产用)
 nohup masterha_manager --conf=/etc/mha/mha_cluster.cnf > /var/log/mha/mha_cluster/nohup.log 2>&1 &
 ​
 # 检查MHA状态
 masterha_check_status --conf=/etc/mha/mha_cluster.cnf
 # 输出"mha_cluster (pid: xxxx) is running(0:PING_OK)"即为正常运行

测试故障切换

1. 模拟Master故障(在mha-node1执行)

 systemctl stop mysqld  # 停止主库服务

2. 观察故障切换(manager节点日志)

 tail -f /var/log/mha/mha_cluster/manager.log
 # 正常情况下,日志会显示:
 # - 检测到master故障
 # - 提升mha-node2为新master
 # - 其他slave(mha-node3)指向新master

3. 验证切换结果

 # 在新master(mha-node2)查看状态
 mysql -uroot -p -e "SELECT @@server_id, @@read_only;"
 # 应显示server_id=51,read_only=O
 ​
 # 在mha-node3查看复制状态
 mysql -uroot -p -e "SHOW SLAVE STATUS\G"
 # 应显示Master_Host为mha-node2,且复制正常

故障恢复后处理

查看当前主库(mha-node2)

 # 查看状态
 mysql -uroot -p -e "SELECT @@server_id, @@hostname, @@read_only;"
 ​
 # 预期效果
 +-------------+------------+-------------+
 | @@server_id | @@hostname | @@read_only |
 +-------------+------------+-------------+
 |          51 | mha-node2  |           0 |
 +-------------+------------+-------------+

查看从库(mha-node3)复制状态

 # 查看状态
 mysql -uroot -p -e "SHOW SLAVE STATUS\G" | grep -E "Master_Host|Slave_IO_Running|Slave_SQL_Running"
 ​
 # 预期效果
 Master_Host: mha-node2
 Slave_IO_Running: Yes
 Slave_SQL_Running: Yes

恢复旧主库(mha-node1)作为从库

获取复制用户密码

 # 在mha-node2上查看配置文件
 grep -i "repl_password" /etc/mha/mha_cluster.cnf
 ​
 # 或在MySQL中重置密码(如果不知道)
 mysql -uroot -p -e "ALTER USER 'repl'@'%' IDENTIFIED BY '123456';"

在mha-node1上配置复制MySQL里面

 STOP SLAVE;
 RESET SLAVE ALL;
 CHANGE MASTER TO
   MASTER_HOST='192.168.7.51',
   MASTER_PORT=3306,
   MASTER_USER='repl',
   MASTER_PASSWORD='123456',
   MASTER_AUTO_POSITION=1;
 START SLAVE;

完成后回到 mha-node2,继续后续步骤

清理故障标记文件

 rm -f /var/log/mha/mha_cluster/mha_cluster.failover.complete

验证复制状态

 masterha_check_repl --conf=/etc/mha/mha_cluster.cnf

重启MHA Manager

 nohup masterha_manager --conf=/etc/mha/mha_cluster.cnf > /var/log/mha/mha_cluster/nohup.log 2>&1 &

确认运行状态

 masterha_check_status --conf=/etc/mha/mha_cluster.cnf