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
相关推荐
IvorySQL15 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·16 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德16 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫16 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i16 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.16 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn16 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露17 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星17 小时前
sql语言之分组语句group by
java·数据库·sql
符哥200817 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql