MySQL 高可用解决方案 MHA:原理、配置与实践

一、什么是 MHA

MHA(Master High Availability)是一套专为 MySQL 设计的高可用解决方案,核心用于解决 MySQL 单点故障问题,提供自动故障切换和主从复制管理能力。

其核心价值在于:

故障切换速度快,可在 0-30 秒内完成自动切换,对应用透明。

最大程度保证数据一致性,故障切换时会尽力保存宕机主库的二进制日志,避免数据丢失。

支持一主多从架构(最少 3 台服务器:一主两从),可管理多组主从复制集群。

二、MHA 核心组成

MHA 由两个关键组件构成,二者协同实现高可用功能:

1. MHA Node(数据节点组件)

部署位置:运行在每台 MySQL 服务器(Master 和所有 Slave 节点)。

核心作用:执行底层数据操作,如保存主库二进制日志、应用中继日志差异、清除中继日志等,无需人工干预,由 Manager 组件触发。

关键工具:

  • save_binary_logs:保存和复制宕机主库的二进制日志。

  • apply_diff_relay_logs:识别从库间中继日志差异并同步。

  • purge_relay_logs:安全清除中继日志(不阻塞 SQL 线程)。

2. MHA Manager(管理节点组件)

部署位置:可单独部署在独立服务器,也可部署在某台 Slave 节点上。

核心作用:监控 Master 节点状态,当主库故障时自动执行故障切换流程,管理整个集群的主从复制关系。

关键工具:

  • masterha_check_ssh:检查各节点间 SSH 无密码认证配置。

  • masterha_check_repl:验证 MySQL 主从复制健康状态。

  • masterha_manager:启动 MHA 管理服务的核心脚本。

  • masterha_master_switch:手动/自动控制故障切换。

  • masterha_check_status:查看 MHA 运行状态及当前主库。

三、MHA 核心特点

  1. 数据高一致性:故障切换时优先保存宕机主库的二进制日志,结合半同步复制,可将数据丢失风险降至最低。

  2. 自动故障切换:无需人工介入,故障后快速提升最新数据的 Slave 为新主库,并重新指向所有从库。

  3. 灵活部署:支持多组主从集群管理,Manager 可独立部署或复用 Slave 节点资源。

  4. 兼容性强:适配 MySQL 5.7 及以上版本,支持基于 position 或 GTID 的主从复制。

四、MHA 实现原理(工作流程)

MHA 的故障切换核心围绕"数据同步→主库选举→集群重构"三个阶段,具体流程如下:

1. 故障检测与日志抢救

Manager 节点通过定时 ping 检测 Master 状态(默认间隔 3 秒,可通过 ping_interval 配置)。

当 Master 宕机时,Manager 触发 Node 组件的 save_binary_logs 工具,从宕机主库抢救未同步的二进制日志(binlog events)。

2. 从库数据一致性校准

Manager 对比所有 Slave 的中继日志(relay log),识别出拥有最新数据的 Slave(通过 position/GTID 判断)。

通过 apply_diff_relay_logs 工具,将最新 Slave 的中继日志差异应用到其他所有 Slave,确保所有从库数据一致。

3. 新主库选举与提升

按优先级算法选择备选主库(优先级:权重配置 > 数据一致性 > 配置文件顺序):

  1. 若 Slave 配置 candidate_master=1check_repl_delay=0,强制选为新主库(忽略复制延迟)。

  2. 无权重配置时,选择数据最接近原主库的 Slave。

  3. 数据一致时,按配置文件中 server 节点顺序选择。

将选中的 Slave 提升为新 Master,关闭其只读模式(set global read_only=0)。

4. 集群重构与 VIP 漂移

所有其他 Slave 重新指向新 Master,执行 change master to 命令同步新主库数据。

通过 master_ip_failover 脚本,将虚拟 IP(VIP)从原主库漂移到新主库,保证应用访问地址不变。

MHA 工作流程示意图

五、实验环境

标准一主两从架构

节点类型 服务器信息 部署组件 IP 地址
MHA Manager CentOS7.6(64位) MHA Node + MHA Manager 192.168.10.80
Master(原主库) CentOS7.6(64位)+ MySQL5.7 MHA Node + MySQL5.7 192.168.10.130
Slave1(备选主) CentOS7.6(64位)+ MySQL5.7 MHA Node + MySQL5.7 192.168.10.131
Slave2(从库) CentOS7.6(64位)+ MySQL5.7 MHA Node + MySQL5.7 192.168.10.132
虚拟 IP(VIP) - 绑定在当前主库网卡 192.168.10.200

六、MHA 搭建实现步骤(CentOS7.6 + MySQL5.7)

1. 环境准备(所有节点)

关闭防火墙和 SELinux:

Bash 复制代码
systemctl stop firewalld && systemctl disable firewalld
setenforce 0 && sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/sysconfig/selinux

安装 MySQL5.7(Master、Slave1、Slave2),并创建软链接:

Bash 复制代码
ln -s /usr/local/mysql/bin/mysql /usr/sbin/
ln -s /usr/local/mysql/bin/mysqlbinlog /usr/sbin/

修改主机名(便于识别):

Bash 复制代码
# Master 节点
hostnamectl set-hostname Master
# Slave1 节点
hostnamectl set-hostname Slave1
# Slave2 节点
hostnamectl set-hostname Slave2

2. 配置 MySQL 主从复制(一主两从)

(1)修改 MySQL 配置文件 /etc/my.cnf

Master 节点:

TOML 复制代码
[mysqld]
server-id = 1  # 唯一ID,不可重复
log_bin = master-bin  # 开启二进制日志
log-slave-updates = true  # 允许从库同步主库日志
binlog_format = mixed  # 二进制日志格式
skip-ssl  # 禁用SSL(yum安装必填)

Slave1 节点:

TOML 复制代码
[mysqld]
server-id = 2
log_bin = master-bin
relay-log = relay-log-bin  # 开启中继日志
relay-log-index = slave-relay-bin.index
binlog_format = mixed
log-slave-updates = true
skip-ssl

Slave2 节点:

TOML 复制代码
[mysqld]
server-id = 3
relay-log = relay-log-bin
relay-log-index = slave-relay-bin.index
binlog_format = mixed
log-slave-updates = true
skip-ssl

重启所有节点 MySQL 服务:systemctl restart mysqld

(2)MySQL 授权配置(Master 节点执行)
Bash 复制代码
mysql -uroot -p
# 授权从库同步用户
grant replication slave on *.* to 'myslave'@'192.168.10.%' identified by '123456';
# 授权 MHA 管理用户
grant all privileges on *.* to 'mha'@'192.168.10.%' identified by 'manager';
# 授权主机名访问(避免从库通过主机名连接失败)
grant all privileges on *.* to 'mha'@'Master' identified by 'manager';
grant all privileges on *.* to 'mha'@'Slave1' identified by 'manager';
grant all privileges on *.* to 'mha'@'Slave2' identified by 'manager';
flush privileges;
(3)启动主从复制

Master 节点查看同步点:

SQL 复制代码
show master status;
-- 记录 File(如 master-bin.000001)和 Position(如 1745)

Slave1、Slave2 节点执行同步命令:

SQL 复制代码
change master to 
master_host='192.168.10.130',  # Master 节点IP
master_user='myslave',
master_password='123456',
master_log_file='master-bin.000001',  # 对应主库 File
master_log_pos=1745;  # 对应主库 Position
start slave;  # 启动同步

验证同步状态(Slave 节点执行):

SQL 复制代码
show slave status\G
-- 确保 Slave_IO_Running=Yes 和 Slave_SQL_Running=Yes

设置从库为只读模式:set global read_only=1;

3. 安装 MHA 组件

(1)安装依赖环境(所有节点)
Bash 复制代码
yum install epel-release --nogpgcheck -y
yum install -y perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker perl-CPAN
(2)安装 MHA Node 组件(所有节点)
Bash 复制代码
cd /opt
tar zxvf mha4mysql-node-0.57.tar.gz
cd mha4mysql-node-0.57
perl Makefile.PL
make && make install
(3)安装 MHA Manager 组件(仅 Manager 节点)
Bash 复制代码
cd /opt
tar zxvf mha4mysql-manager-0.57.tar.gz
cd mha4mysql-manager-0.57
perl Makefile.PL
make && make install

4. 配置无密码 SSH 认证(所有节点间互信)

以 Manager 节点为例,配置到所有数据节点的互信:

Bash 复制代码
ssh-keygen -t rsa  # 一路回车,生成密钥
ssh-copy-id 192.168.10.130  # Master
ssh-copy-id 192.168.10.131  # Slave1
ssh-copy-id 192.168.10.132  # Slave2

同理,Master、Slave1、Slave2 节点需分别配置到其他两个数据节点的 SSH 互信。

5. MHA 核心配置(Manager 节点)

(1)复制故障切换脚本
Bash 复制代码
# 复制示例脚本到 /usr/local/bin
cp -rp /opt/mha4mysql-manager-0.57/samples/scripts /usr/local/bin
# 复制 VIP 管理脚本
cp /usr/local/bin/scripts/master_ip_failover /usr/local/bin
cp /usr/local/bin/scripts/master_ip_online_change /usr/local/bin
(2)修改 VIP 管理脚本 master_ip_failover
Bash 复制代码
vim /usr/local/bin/master_ip_failover
# 替换原有内容为以下配置(修改 VIP、网卡等参数)
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
my (
$command, $ssh_user, $orig_master_host, $orig_master_ip,
$orig_master_port, $new_master_host, $new_master_ip, $new_master_port
);
# VIP 配置参数
my $vip = '192.168.10.200';  # 虚拟IP
my $brdc = '192.168.10.255';  # 广播地址
my $ifdev = 'ens33';  # 绑定网卡(根据实际修改)
my $key = '1';  # 虚拟网卡序列号
my $ssh_start_vip = "/sbin/ifconfig $ifdev:$key $vip";  # 启动VIP命令
my $ssh_stop_vip = "/sbin/ifconfig $ifdev:$key down";  # 停止VIP命令
my $exit_code = 0;
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,
'new_master_host=s' => \$new_master_host,
'new_master_ip=s' => \$new_master_ip,
'new_master_port=i' => \$new_master_port,
);
exit &main();
sub main {
print "\n\nIN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===\n\n";
if ( $command eq "stop" || $command eq "stopssh" ) {
eval {&stop_vip();$exit_code=0;};
if ($@) {warn "Got Error: $@\n";exit 1;}
exit $exit_code;
}
elsif ( $command eq "start" ) {
eval {&start_vip();$exit_code=0;};
if ($@) {warn $@;exit 10;}
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 usage {print "Usage: master_ip_failover --command=start|stop|stopssh|status --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";}
(3)创建 MHA 配置文件 app1.cnf
Bash 复制代码
mkdir /etc/masterha
cp /opt/mha4mysql-manager-0.57/samples/conf/app1.cnf /etc/masterha/
vim /etc/masterha/app1.cnf
# 替换内容如下
[server default]
manager_log=/var/log/masterha/app1/manager.log  # 日志路径
manager_workdir=/var/log/masterha/app1  # 工作目录
master_binlog_dir=/usr/local/mysql/data  # 主库二进制日志路径(yum安装为/var/lib/mysql)
master_ip_failover_script=/usr/local/bin/master_ip_failover  # VIP切换脚本
master_ip_online_change_script=/usr/local/bin/master_ip_online_change
password=manager  # MHA管理用户密码
ping_interval=1  # 主库心跳检测间隔(1秒)
remote_workdir=/tmp  # 远端binlog保存目录
repl_password=123456  # 从库同步用户密码
repl_user=myslave  # 从库同步用户
secondary_check_script=/usr/local/bin/masterha_secondary_check -s 192.168.10.131 -s 192.168.10.132  # 从库检查
shutdown_script=""  # 关闭故障主库脚本(可选)
ssh_user=root  # SSH登录用户
user=mha  # MHA管理用户

[server1]
hostname=192.168.10.130  # Master节点IP
port=3306

[server2]
hostname=192.168.10.131  # Slave1节点IP(备选主库)
port=3306
candidate_master=1  # 标记为候选主库
check_repl_delay=0  # 忽略复制延迟

[server3]
hostname=192.168.10.132  # Slave2节点IP
port=3306

6. 启动与验证 MHA

(1)手动开启 Master 节点 VIP
Bash 复制代码
# 在 Master 节点执行
/sbin/ifconfig ens33:1 192.168.10.200/24
(2)测试 SSH 互信配置
Bash 复制代码
# 在 Manager 节点执行
masterha_check_ssh -conf=/etc/masterha/app1.cnf
# 输出 "All SSH connection tests passed successfully" 即为正常
(3)测试主从复制健康状态
Bash 复制代码
# 在 Manager 节点执行
masterha_check_repl -conf=/etc/masterha/app1.cnf
# 输出 "MySQL Replication Health is OK" 即为正常
(4)启动 MHA 服务
Bash 复制代码
nohup masterha_manager --conf=/etc/masterha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/masterha/app1/manager.log 2>&1 &

参数说明:

--remove_dead_master_conf:故障切换后自动删除原主库配置。

--ignore_last_failover:忽略上次故障切换记录(避免8小时内无法重复切换)。

(5)查看 MHA 状态
Bash 复制代码
# 查看当前主库和 MHA 运行状态
masterha_check_status --conf=/etc/masterha/app1.cnf
# 查看日志确认主库
cat /var/log/masterha/app1/manager.log | grep "current master"
# 查看 VIP 是否绑定
ifconfig  # 确认 ens33:1 对应 VIP 192.168.10.200



七、故障模拟与修复

1. 模拟主库故障

在 Master 节点停止 MySQL 服务(模拟宕机):

Bash 复制代码
systemctl stop mysqld 或 pkill -9 mysql

在 Manager 节点监控故障切换日志:

Bash 复制代码
tail -f /var/log/masterha/app1/manager.log

切换成功特征:

  1. MHA 进程自动退出(单次切换后)。

  2. Slave1 节点接管 VIP(执行 ifconfig 可见 ens33:1 绑定 192.168.10.200 。

  3. app1.cnf 中自动删除原 Master 节点(server1)配置。

2. 故障修复与集群重构

(1)修复原 Master 节点 MySQL 服务
Bash 复制代码
# 在原 Master 节点执行
systemctl restart mysqld
(2)重新配置主从复制(原 Master 变为从库)

在新主库(Slave1,<192.168.10.131>)查看同步点:

SQL 复制代码
show master status;
-- 记录 File(如 master-bin.000002)和 Position(如 154)

在原 Master 节点执行同步命令:

SQL 复制代码
change master to 
master_host='192.168.10.131',  # 新主库IP(Slave1)
master_user='myslave',
master_password='123456',
master_log_file='master-bin.000002',  # 新主库 File
master_log_pos=154;  # 新主库 Position
start slave;

验证同步状态:show slave status\G(确保 IO 和 SQL 线程为 Yes)。

(3)恢复 MHA 配置并重启

在 Manager 节点修改 app1.cnf,重新添加原 Master 节点配置:

Bash 复制代码
vim /etc/masterha/app1.cnf
# 恢复 server1 配置
[server1]
hostname=192.168.10.130  # 原 Master 节点IP
port=3306

重启 MHA 服务:

Bash 复制代码
nohup masterha_manager --conf=/etc/masterha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/masterha/app1/manager.log 2>&1 &

八、关键命令汇总

操作目的 命令
查看 MHA 状态 masterha_check_status --conf=/etc/masterha/app1.cnf
停止 MHA 服务 masterha_stop --conf=/etc/masterha/app1.cnf 或 kill -9 进程ID
查看故障切换日志 tail -f /var/log/masterha/app1/manager.log
手动触发故障切换 masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=dead
验证 SSH 互信 masterha_check_ssh -conf=/etc/masterha/app1.cnf
验证主从复制健康 masterha_check_repl -conf=/etc/masterha/app1.cnf
相关推荐
一 乐1 小时前
高校评教|基于SpringBoot+vue高校学生评教系统 (源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
翔云1234562 小时前
在MySQL中,gtid_purged 的初始化和更新机制
数据库·mysql
smileNicky2 小时前
大型MySQL查询优化实战:从全表扫描到毫秒级响应的通用索引设计
数据库·mysql
武帝为此2 小时前
【Redis 数据库介绍】
数据库·redis·缓存
Elastic 中国社区官方博客3 小时前
Elasticsearch:数据脱节如何破坏现代调查
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
Dxy12393102163 小时前
MySQL性能优化深度解析
数据库·mysql·性能优化
铁锚3 小时前
Redis中KEYS命令的潜在风险与遍历建议
数据库·redis·缓存
清晓粼溪3 小时前
MySQL-01:基础知识
数据库·mysql
贝塔实验室3 小时前
Altium Designer 集成库介绍
arm开发·数据库·驱动开发·硬件工程·射频工程·基带工程·精益工程