MySQL MHA 高可用集群搭建过程详解
作者:云边有个小代码
一、MHA 是什么?
MHA 是一套优秀的、开源的 MySQL 高可用性解决方案。它的核心目标是在 MySQL 主从复制环境中,实现主库故障时的自动故障转移和快速切换,尽可能保证数据库服务的连续性。
MHA 的组成
一个典型的 MHA 集群包含以下组件:
- MySQL Master:主库,负责读写。
- MySQL Slave:一个或多个从库,复制主库的数据。
- MHA Manager:管理节点。这是 MHA 的核心,通常部署在一台独立的服务器上。它负责监控所有 MySQL 节点,并在主库故障时执行故障转移操作。
- MHA Node:数据节点。由主库以及从库共同组成,这是一个运行在每个 MySQL 服务器(无论是主库还是从库)上的脚本集合。它负责执行 Manager 发出的具体命令,比如切换虚拟 IP、对比日志等。
MHA 的核心工作原理
MHA 的工作原理可以概括为:一个聪明的“大脑”(Manager)时刻监控着整个 MySQL 集群,一旦“首领”(Master)倒下,它会立刻根据数据的新旧程度选出一个最合适的“新首领”,并设法找回“老首领”没来得及交代的“遗言”(未同步的日志),最终让所有“部下”(Slaves)效忠于“新首领”,从而快速恢复秩序。
MHA 的工作流程可以分为两大阶段:日常监控阶段 和 故障转移阶段。
阶段一:日常监控
- Manager 监控所有节点:MHA Manager 会周期性地(例如每秒一次)通过 SSH 连接到所有的 MySQL 节点(Master 和 Slaves),执行
SELECT 1之类的简单查询来检查它们的存活状态。 - 收集复制拓扑信息:Manager 会从各个节点获取关键的复制信息,如:
- 谁是当前的主库?
- 谁是从库?
- 每个从库的复制位置(Master_Log_File, Read_Master_Log_Pos)是多少?
这个阶段,Manager 对整个集群的健康状况和结构了如指掌。
阶段二:故障转移(核心)
当 Manager 检测到主库无法连接或已经宕机时,它会自动触发故障转移流程。这个过程非常精细,是其高可靠性的关键。
第1步:验证主库故障
- Manager 会多次尝试连接故障的主库,确认其是否真的不可用,而不是因为网络抖动。
第2步:选择新的主库
- Manager 会根据预设的规则,从存活的从库中选举出一个新的主库。
- 选举策略通常基于:
- 配置优先级:在配置文件中明确指定候选主库的优先级。
- 数据最新程度:比较所有从库的复制位置,选择拥有最新数据的从库(即延迟最小的那个)作为新主库。这是 MHA 最关键的特性之一,它最大程度地保证了数据不丢失。
第3步:补偿丢失的数据(关键步骤)
- 这是 MHA 的精髓所在。由于主库是突然宕机的,可能有一些已经提交的二进制日志事件(binlog events)没有被任何从库接收到。这些数据就“丢失”了。
- MHA 会尝试从宕机主库的服务器上直接拷贝这些未发送的二进制日志(如果服务器磁盘可访问,比如硬件故障但磁盘完好)。
- 然后,MHA 将这些获取到的二进制日志应用到新选出的主库上。这样就最大限度地保证了数据的一致性,将数据丢失降到最低。
第4步:切换从库到新主库
- Manager 通过 MHA Node 命令所有的其他从库指向新的主库,并重新开始复制(
CHANGE MASTER TO ...)。
第5步:虚拟 IP 切换(可选但常用)
- 为了使应用程序无感知,通常会配合使用虚拟 IP。
- Manager 会将原来指向旧主库的虚拟 IP 重新绑定到新的主库上。这样,应用程序无需修改任何配置,就能自动连接到新的主库。
第6步:通知管理员(可选)
- 故障转移完成后,MHA 可以通过邮件、短信等方式通知管理员,以便进行后续的检查和维护。
至此,整个故障转移过程完成,集群恢复了服务。
MHA 的优势
- 数据一致性高:通过补偿未发送的 binlog,最大程度避免数据丢失。
- 故障转移快速:通常在 10-30 秒内即可完成自动切换。
- 自动化和易用性:无需人工干预,提供了完善的管理命令。
- 对现有环境无侵入:MHA 只是在现有主从复制基础上增加了一个管理层面,不需要对 MySQL 本身做任何修改。
- 支持 GTID 和传统复制:兼容性好。
MHA 的局限性
- 需要脚本和 SSH 信任:部署相对复杂,需要配置节点间的 SSH 免密登录。
- 单点 Manager:MHA Manager 本身是单点,需要额外的手段来保证其高可用(比如部署备用 Manager 或使用 Keepalived)。
- 自愈能力有限:通常不提供自动将旧主库重新拉回集群的功能,需要管理员手动处理。
二、搭建MHA
实验思路:
1.MHA架构
1)数据库安装
2)一主两从
3)MHA搭建
2.故障模拟
1)主库失效
2)备选主库成为主库
3)原故障主库恢复重新加入到MHA成为从库
架构图

1.环境准备
节点服务器 所需服务及组件 IP地址 主机名
| 角色 | 安装组件 | IP地址 | 主机名 |
|---|---|---|---|
| MHA manager | 安装MHA node 和 manager 组件 | 192.168.4.54 | host54 |
| Master | 安装mysql5.7、MHA node 组件 | 192.168.4.51 | host51 |
| Slave1 | 安装mysql5.7、MHA node 组件 | 192.168.4.52 | host52 |
| Slave2 | 安装mysql5.7、MHA node 组件 | 192.168.4.53 | host53 |
关闭防火墙,selinux
systemctl stop firewalld systemctl disable firewalld setenforce 0
2.配置主机名和 Hosts 解析
分别编辑主机名
hostnamectl set-hostname host51 #4.51主机上执行 hostnamectl set-hostname host52 #4.52主机上执行 hostnamectl set-hostname host53 #4.53主机上执行 hostnamectl set-hostname host54 #4.54主机上执行
编辑所有节点的 /etc/hosts 文件,添加以下内容
echo "192.168.4.51 host51" >> /etc/hosts echo "192.168.4.52 host52" >> /etc/hosts echo "192.168.4.53 host53" >> /etc/hosts echo "192.168.4.54 host54" >> /etc/hosts
3.在所有数据库节点安装MySQL
示范在host51节点安装,另外两个节点同理
[root@host51 ~]# tar -xvf mysql-5.7.17.tar #v:显示详细信息
[root@host51 ~]# yum -y install mysql-community-*.rpm
[root@host51 ~]# systemctl restart mysqld #启动服务
[root@host51 ~]# systemctl enable mysqld
[root@host51 ~]# ss -antlp | grep 3306
[root@host51 ~]# ps -C mysqld #查看进程连接数据库
使用初始密码登录
[root@host51 ~]# grep password /var/log/mysqld.log #获取初始密码
#重置密码
[root@host51 ~]# mysqladmin -uroot -p'qg1wpZ;G+deg' password '123qqq...A'
[root@host51 ~]# mysql -uroot -p123qqq...A4.配置 SSH 免密登录
在 MHA Manager 节点上生成密钥对,并分发到所有 MySQL 节点。
[root@host54 ~]# ssh-keygen -f /root/.ssh/id_rsa -N '' [root@host54 ~]# ssh-copy-id root@host51 [root@host54 ~]# ssh-copy-id root@host52 [root@host54 ~]# ssh-copy-id root@host53
MHA Manager 需要通过 SSH 连接到所有 MySQL 节点执行管理命令(如启动、停止、复制状态检查等),免密登录是必须的。
数据库节点之间也必须配置 SSH 免密码登录,这是 MHA 正常工作的关键要求。
[root@host51 ~]# ssh-keygen -f /root/.ssh/id_rsa -N '' [root@host51 ~]# ssh-copy-id root@host52 [root@host51 ~]# ssh-copy-id root@host53 [root@host52 ~]# ssh-keygen -f /root/.ssh/id_rsa -N '' [root@host52 ~]# ssh-copy-id root@host51 [root@host52 ~]# ssh-copy-id root@host53 [root@host53 ~]# ssh-keygen -f /root/.ssh/id_rsa -N '' [root@host53 ~]# ssh-copy-id root@host51 [root@host53 ~]# ssh-copy-id root@host52
5.配置 MySQL Master 节点
1)修改 MySQL 配置文件 /etc/my.cnf
在mysqld下添加两行
[mysqld] server_id=51 log_bin=master51 relay-log-purge = 0 # 禁止自动删除中继日志(控制是否自动删除不再需要的中继日志)
重启mysqld服务
systemctl restart mysqld
2)创建授权用户
mysql> grant replication slave on *.* to repluser@"%" identified by '123qqq...A'; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> show master status; +-----------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-----------------+----------+--------------+------------------+-------------------+ | master51.000001 | 441 | | | | +-----------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
6.配置 MySQL Slave 节点 (host52 & host53)
1)修改 MySQL 配置文件 /etc/my.cnf:
在mysqld下添加server_id
host52:
[mysqld] server_id=52 log_bin=master52 relay-log-purge = 0 # 禁止自动删除中继日志(控制是否自动删除不再需要的中继日志)
host53:
[mysqld] server_id=53 log_bin=master53 relay-log-purge = 0 # 禁止自动删除中继日志(控制是否自动删除不再需要的中继日志)
重启mysqld服务
[root@host52 ~]# systemctl restart mysqld [root@host53 ~]# systemctl restart mysqld
2)指定主服务器信息并启动slave
host52:
mysql> change master to master_host='192.168.4.51',master_user='repluser',master_password='123qqq...A',master_log_file='master51.000001',master_log_pos=441;
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.4.51
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master51.000001
Read_Master_Log_Pos: 441
Relay_Log_File: host52-relay-bin.000002
Relay_Log_Pos: 319
Relay_Master_Log_File: master51.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
....................host53:
mysql> change master to master_host="192.168.4.51",master_user="repluser",master_password="123qqq...A",master_log_file="master51.000001",master_log_pos=441;
Query OK, 0 rows affected, 2 warnings (0.02 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.4.51
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master51.000001
Read_Master_Log_Pos: 441
Relay_Log_File: host53-relay-bin.000002
Relay_Log_Pos: 319
Relay_Master_Log_File: master51.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
.................... 在所有slave节点也授权
grant replication slave on *.* to repluser@"%" identified by "123qqq...A";
7.安装 MHA Node
MHA Node 需要在所有 MySQL 数据库节点上安装
MHA Node 是运行在每台 MySQL 服务器上的代理。
mha-node资源包网址:
https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58-0.el7.centos.noarch.rpm
# 安装依赖 # 如果 ncftp 不可用,可以跳过 [root@host51 mysql]# yum -y install perl-DBD-MySQL ncftp perl-DBIx-Simple [root@host52 mysql]# yum -y install perl-DBD-MySQL nfctp perl-DBIx-Simple [root@host53 mysql]# yum -y install perl-DBD-MySQL nfctp perl-DBIx-Simple # 使用 Yum 安装 MHA Node [root@host51 mysql]# yum -y install mha4mysql-node-0.58-0.el7.centos.noarch.rpm [root@host52 mysql]# yum -y install mha4mysql-node-0.58-0.el7.centos.noarch.rpm [root@host53 mysql]# yum -y install mha4mysql-node-0.58-0.el7.centos.noarch.rpm
MHA Node 包含了 save_binary_logs, apply_diff_relay_logs 等关键脚本,用于故障切换时保存和应用日志。
8.在Manager节点上启用epel仓库:
为什么需要 EPEL?
perl-Parallel-ForkManager、perl-Config-IniFiles、ncftp 这些包在 CentOS 默认仓库中不可用
EPEL 仓库提供了这些额外的软件包
先启用 EPEL 可以避免 “No package available” 错误
注意!下载epel仓库需要联网,可以给manage节点设置双网卡,其中一个网卡设置为NAT模式
下载 EPEL 的 RPM 包
EPEL 的官方地址可能会变,但你可以去这个链接下载最新的适用于 CentOS 7 的 EPEL 包:
安装包下载:https://pan.quark.cn/s/956399e9b728
启用 EPEL 仓库 yum -y install epel-release-latest-7.noarch.rpm yum clean all yum repolist
9.安装 MHA Manager
仅在 mha-manager 节点上执行
MHA Manager 是管理核心。
mha-manager资源包网址:
https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
mha-node资源包网址:
https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58-0.el7.centos.noarch.rpm
# 安装依赖 yum install -y perl-DBD-MySQL perl-Config-IniFiles perl-Time-HiRes perl-Parallel-ForkManager ncftp perl-Log-Dispatch perl-DBI mha4mysql-node-0.58-0.el7.centos.noarch.rpm yum -y install mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
Manager 会监控 Node 的状态,并在 Master 故障时协调故障切换过程
10.配置 MHA
创建 MHA 工作目录和配置文件
在 mha-manager 节点上操作。
mkdir -p /etc/mha mkdir -p /var/log/mha
创建主配置文件 /etc/mha/app1.cnf:
[server default] # [server default] 段 - 全局默认配置 # MySQL 用户和密码 # MHA Manager 连接 MySQL 节点时使用的用户名和密码 # 需要在所有 MySQL 节点上创建这个用户并授予相应权限 user=mha password=123qqq...A # MySQL 主从复制使用的用户名和密码 # 用于在故障切换时在新主库上配置复制关系 repl_user=repluser repl_password=123qqq...A # SSH 配置 ssh_user=root # MHA Manager 工作目录 manager_workdir=/var/log/mha manager_log=/var/log/mha/manager.log # 远程工作目录(在 MySQL 节点上) remote_workdir=/var/log/mha # 主从复制相关配置 ping_interval=3 # 二次检查,防止脑裂 secondary_check_script=masterha_secondary_check -s host52 -s host53 -s host51 master_binlog_dir=/var/lib/mysql # 故障切换脚本(可选) #master_ip_failover_script=/etc/mha/master_ip_failover [server1] hostname=host51 candidate_master=1 [server2] hostname=host52 candidate_master=1 [server3] hostname=host53 no_master=1 # 这个节点永远不会被提升为新的 Master
解释:
[server default]:全局默认配置。
secondary_check_script:非常重要,通过多个 Slave 来确认 Master 是否真的宕机,防止网络分区导致的脑裂。
candidate_master=1:优先级高,在故障切换时优先被提升为新主。
no_master=1:标记该节点不应被提升为主库(例如,配置较低或用于备份)。
在master主库授权mha用户
mysql> grant all on *.* to mha@"%" identified by '123qqq...A'; Query OK, 0 rows affected, 1 warning (0.03 sec)
11.检查 MHA 配置
# 检查 SSH 连接 masterha_check_ssh --conf=/etc/mha/app1.cnf # 检查主从复制状态 masterha_check_repl --conf=/etc/mha/app1.cnf
12.启动 MHA Manager
mkdir -p /var/log/mha nohup masterha_manager --conf=/etc/mha/app1.cnf < /dev/null > /var/log/mha/manager.log 2>&1 &
13.检查 MHA 状态
[root@host54 ~]# masterha_check_status --conf=/etc/mha/app1.cnf app1 (pid:12835) is running(0:PING_OK), master:host51
14.故障切换流程(无 VIP 版本)
自动切换过程
MHA 检测到 Master 宕机
自动选择最优 Slave(host52)提升为新 Master
其他 Slave(host53)重新指向新 Master
MHA Manager 记录切换日志并自动停止
1)手动宕机master
停止 host51 的 MySQL 服务
在master节点执行 systemctl stop mysqld
2)查看管理节点日志
在 host54 上验证 [root@host54 ~]# tail -f /var/log/mha/manager.log Started automated(non-interactive) failover. The latest slave host52(192.168.4.52:3306) has all relay logs for recovery. Selected host52(192.168.4.52:3306) as a new master. host52(192.168.4.52:3306): OK: Applying all logs succeeded. host53(192.168.4.53:3306): This host has the latest relay log events. Generating relay diff files from the latest slave succeeded. host53(192.168.4.53:3306): OK: Applying all logs succeeded. Slave started, replicating from host52(192.168.4.52:3306) host52(192.168.4.52:3306): Resetting slave info succeeded. Master failover to host52(192.168.4.52:3306) completed successfully.
3)检查新主库状态
[root@host53 mysql]# mysql -h host52 -u mha -p123qqq...A -e "show master status" mysql: [Warning] Using a password on the command line interface can be insecure. +-----------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-----------------+----------+--------------+------------------+-------------------+ | master52.000001 | 441 | | | | +-----------------+----------+--------------+------------------+-------------------+
4)检查从库复制状态
[root@host52 mysql]# mysql -h host53 -u mha -p123qqq...A -e "show slave status\G"
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.4.52
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master52.000001
Read_Master_Log_Pos: 441
Relay_Log_File: host53-relay-bin.000002
Relay_Log_Pos: 319
Relay_Master_Log_File: master52.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
.............................. 5)插入数据测试验证
在新主库 host52 上插入数据
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema | |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> create database d2;
Query OK, 1 row affected (0.02 sec)
mysql> create table d2.student(name char(10),number int default 10);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into d2.student values("bob",1);
Query OK, 1 row affected (0.01 sec)在从库 host53 上验证数据同步
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | | d2 | | mysql | | performance_schema | | sys | +--------------------+ 6 rows in set (0.00 sec) mysql> select * from d2.student; +------+--------+ | name | number | +------+--------+ | bob | 1 | +------+--------+ 1 row in set (0.00 sec)
6)恢复旧 Master
因为在host51宕机期间,现在的master节点插入了数据,它们之间有数据差异,所以这里需要先同步数据
备份数据
[root@host52 ~]# mysqldump -uroot -p123456 --all-databases --master-data=1 > backup.sql [root@host52 ~]# scp backup.sql host51:/root/
host51同步数据
[root@host51 ~]# systemctl start mysqld [root@host51 ~]# mysql -uroot -p123456 < backup.sql [root@host51 ~]# mysql -uroot -p123456 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | | d2 | | | mysql | | performance_schema | | sys | +--------------------+ 7 rows in set (0.00 sec) mysql> select * from d2.student; +------+--------+ | name | number | +------+--------+ | bob | 1 | +------+--------+ 1 row in set (0.00 sec)
查看当前master host52的status
mysql> show master status; +-----------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-----------------+----------+--------------+------------------+-------------------+ | master52.000001 | 1046 | | | | +-----------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
host51配置为当前 Master 的从库:
-- 在旧 Master 上执行
mysql> change master to master_host="192.168.4.52", master_user="repluser", master_password="123qqq...A", master_log_file="master52.000001", master_log_pos=1046;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.4.52
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master52.000001
Read_Master_Log_Pos: 1046
Relay_Log_File: host51-relay-bin.000002
Relay_Log_Pos: 319
Relay_Master_Log_File: master52.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes7)验证旧master
在host52上插入数据
mysql> create database d3;
Query OK, 1 row affected (0.01 sec)
mysql> create table d3.student(name char(10),number int default 10);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into d3.student values("tom",2);
Query OK, 1 row affected (0.00 sec)在host51上检查数据
mysql> select * from d3.student; +------+--------+ | name | number | +------+--------+ | tom | 2 | +------+--------+ 1 row in set (0.01 sec)
到此,mha高可用集群搭建完毕
到此这篇关于MySQL MHA 高可用集群搭建指南的文章就介绍到这了,更多相关MySQL MHA 高可用集群内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
