第10章 MySQL高可用之MHA
MHA高可用架构
1. MHA介绍与架构设计
MHA概述
MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案。它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。
在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
MHA还提供在线主库切换的功能,能够安全地切换当前运行的主库到一个新的主库中(通过将从库提升为主库),大概0.5-2秒内即可完成。
MHA架构特点
MHA采用Manager/Node架构:
- Manager节点:负责监控、故障检测、选主决策
- Node节点:部署在所有MySQL服务器上,执行具体的故障切换操作
- SSH互信:基于SSH实现各节点间的通信和日志传输
MHA优势
- 自动故障转移快:通常在10-30秒内完成故障切换
- 主库崩溃不存在数据一致性问题:能最大程度保证数据不丢失
- 配置影响小:不需要对当前mysql环境做重大修改
- 资源占用少:仅一台manager就可管理上百个replication
- 性能优秀:可工作在半同步复制和异步复制,当监控mysql状态时,仅需要每隔N秒向master发送ping包(默认3秒),所以对性能无影响。你可以理解为MHA的性能和简单的主从复制框架性能一样
- 存储引擎兼容:只要replication支持的存储引擎,MHA都支持,不会局限于innodb
MHA核心功能
- 监控:实时监控主库状态
- 选主:根据数据最新程度选择新主库
- 应用透明(vip):实现应用透明切换
- 故障提醒:邮件或其他方式通知管理员
- 额外数据补偿:通过binlog server补偿数据差异
- 剔除故障节点:自动剔除故障节点
- manager程序"自杀":Manager程序异常时自动退出避免脑裂
MHA组件功能说明
Manager组件:
masterha_manger
- 启动MHAmasterha_check_ssh
- 检查MHA的SSH配置状况masterha_check_repl
- 检查MySQL复制状况,配置信息masterha_master_monitor
- 检测master是否宕机masterha_check_status
- 检测当前MHA运行状态masterha_master_switch
- 控制故障转移(自动或者手动)masterha_conf_host
- 添加或删除配置的server信息
Node组件:
save_binary_logs
- 保存和复制master的二进制日志apply_diff_relay_logs
- 识别差异的中继日志事件并将其差异的事件应用于其他的purge_relay_logs
- 清除中继日志(不会阻塞SQL线程)
MHA工作原理
MHA Manager和MHA Node的协作流程:
- 监控阶段:MHA Manager定期通过SSH连接到各个MHA Node,获取MySQL实例的状态信息
- 故障检测:如果MHA Manager检测到主库故障,它会通过SSH通知相关MHA Node开始故障转移
- 故障转移:MHA Manager选择一个从库作为新的主库,并通过SSH通知该从库的MHA Node执行提升操作。其他从库的MHA Node会重新配置复制源,指向新的主库
- 数据修复:如果有从库数据不一致,MHA Manager会通过SSH通知相关MHA Node修复数据
- 切换完成:MHA Manager更新配置并通知应用连接新的主库
选主策略
MHA选主遵循以下优先级:
- 日志量最新
- 备选主
- 不被选主
故障转移流程
- 从宕机崩溃的master保存二进制日志事件(binlog events)
- 识别含有最新更新的slave
- 应用差异的中继日志(relay log)到其他的slave
- 应用从master保存的二进制日志事件(binlog events)
- 提升一个slave为新的master
- 使其他的slave连接新的master进行复制
2. MHA部署与配置
部署环境说明
三个MySQL节点,GTID,主从复制
MySQL环境准备
# 1.db-51主库清理
systemctl stop mysqld
rm -rf /data/mysql_3306/*
rm -rf /data/binlogs/*
cat> /etc/my.cnf <<EOF
[mysqld]
port=3306
user=mysql
basedir=/opt/mysql
datadir=/data/mysql_3306
server_id=51
log_bin=/data/binlogs/mysql-bin
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
socket=/tmp/mysql.sock
[mysql]
socket=/tmp/mysql.sock
[client]
socket=/tmp/mysql.sock
EOF
mysqld --initialize-insecure --user=mysql --basedir=/opt/mysql --datadir=/data/mysql_3306/
systemctl start mysqld
mysqladmin password 123
mysql -uroot -p123 -e "reset master;"
# 2.搭建从库
# db52-操作
systemctl stop mysqld
rm -rf /data/mysql_3306/*
rm -rf /data/binlogs/*
cat> /etc/my.cnf <<EOF
[mysqld]
port=3306
user=mysql
basedir=/opt/mysql
datadir=/data/mysql_3306
server_id=52
log_bin=/data/binlogs/mysql-bin
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
socket=/tmp/mysql.sock
[mysql]
socket=/tmp/mysql.sock
[client]
socket=/tmp/mysql.sock
EOF
mysqld --initialize-insecure --user=mysql --basedir=/opt/mysql --datadir=/data/mysql_3306/
systemctl start mysqld
mysqladmin password 123
mysql -uroot -p123 -e "reset master;"
# db-53操作
cd /opt/
tar zxf mysql-5.7.28-linux-glibc2.12-x86_64.tar.gz -C /opt/
mv mysql-5.7.28-linux-glibc2.12-x86_64 mysql-5.7.28
ln -s mysql-5.7.28 mysql
echo 'PATH=$PATH:/opt/mysql/bin' >> /etc/profile
source /etc/profile
mysql -V
rpm -qa|grep mariadb
yum remove mariadb-libs -y
rm -rf /etc/my.cnf
yum install -y libaio-devel
mkdir /data/{mysql_3306,binlogs} -p
useradd -s /sbin/nologin -M mysql
chown -R mysql:mysql /data/
chown -R mysql:mysql /opt/mysql*
cat> /etc/my.cnf <<EOF
[mysqld]
port=3306
user=mysql
basedir=/opt/mysql
datadir=/data/mysql_3306
server_id=53
log_bin=/data/binlogs/mysql-bin
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
socket=/tmp/mysql.sock
[mysql]
socket=/tmp/mysql.sock
[client]
socket=/tmp/mysql.sock
EOF
mysqld --initialize-insecure --user=mysql --basedir=/opt/mysql --datadir=/data/mysql_3306/
cp /opt/mysql/support-files/mysql.server /etc/init.d/mysqld
chkconfig --add mysqld
systemctl start mysqld
netstat -lntup|grep 3306
mysqladmin password 123
mysql -uroot -p123 -e "reset master;"
生产环境建议
生产环境应使用 --initialize
而非 --initialize-insecure
,确保有初始密码。
主从复制环境配置
# db-51创建复制用户
grant replication slave on *.* to repl@'10.0.0.%' identified by '123';
# db-52和db-53配置复制信息
change master to
master_host='10.0.0.51',
master_user='repl',
master_password='123' ,
MASTER_AUTO_POSITION=1;
start slave;
# 检查复制状态
show slave status\G
MHA服务搭建部署
创建软连接:
ln -s /opt/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
ln -s /opt/mysql/bin/mysql /usr/bin/mysql
rm -rf /root/.ssh/
节点互信配置(所有机器都操作):
yum install sshpass -y
ssh-keygen -f /root/.ssh/id_rsa -N ''
sshpass -p '123' ssh-copy-id 10.0.0.51 -o StrictHostKeyChecking=no
sshpass -p '123' ssh-copy-id 10.0.0.52 -o StrictHostKeyChecking=no
sshpass -p '123' ssh-copy-id 10.0.0.53 -o StrictHostKeyChecking=no
各节点验证(所有机器都操作):
ssh 10.0.0.51 hostname
ssh 10.0.0.52 hostname
ssh 10.0.0.53 hostname
所有节点安装node软件:
yum install perl-DBD-MySQL -y
rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm
db-53安装Manager软件:
yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
yum install -y mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
在db-51主库中创建mha需要的用户:
grant all privileges on *.* to mha@'10.0.0.%' identified by 'mha';
select user,host from mysql.user;
Manager配置文件准备
# 创建配置文件目录
mkdir -p /etc/mha
# 创建日志目录
mkdir -p /var/log/mha/app1
# 编辑mha配置文件
cat > /etc/mha/app1.cnf <<EOF
[server default]
manager_log=/var/log/mha/app1/manager
manager_workdir=/var/log/mha/app1
master_binlog_dir=/data/binlogs/
user=mha
password=mha
ping_interval=2
repl_password=123
repl_user=repl
ssh_user=root
[server1]
hostname=10.0.0.51
port=3306
[server2]
hostname=10.0.0.52
port=3306
[server3]
hostname=10.0.0.53
port=3306
EOF
# 配置文件解释
[server default]
manager_log=/var/log/mha/app1/manager # MHA的工作日志设置
manager_workdir=/var/log/mha/app1 # MHA工作目录
master_binlog_dir=/data/binlog # 主库的binlog目录
user=mha # 监控用户
password=mha # 监控密码
ping_interval=2 # 心跳检测的间隔时间
repl_password=123 # 复制用户
repl_user=repl # 复制密码
ssh_user=root # ssh互信的用户
[server1] # 节点信息....
配置说明
manager_log
: MHA的工作日志设置manager_workdir
: MHA工作目录master_binlog_dir
: 主库的binlog目录ping_interval
: 心跳检测的间隔时间repl_user/repl_password
: 复制用户信息
状态检查
masterha_check_ssh --conf=/etc/mha/app1.cnf
masterha_check_repl --conf=/etc/mha/app1.cnf
开启MHA Manager
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
查看MHA状态
[root@db-53 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:12009) is running(0:PING_OK), master:10.0.0.51
3. MHA故障切换实战
VIP故障转移配置
上传VIP脚本
# 上传脚本文件到/usr/local/bin
\cp -a * /usr/local/bin
修改权限
chmod +x /usr/local/bin/*
替换字符
dos2unix /usr/local/bin/*
修改VIP配置
vim /usr/local/bin/master_ip_failover
my $vip = '10.0.0.55/24';
my $key = '1';
my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig eth0:$key down";
my $ssh_Bcast_arp= "/sbin/arping -I eth0 -c 3 -A 10.0.0.55";
更新MHA配置
vim /etc/mha/app1.cnf
master_ip_failover_script=/usr/local/bin/master_ip_failover
重启MHA
masterha_stop --conf=/etc/mha/app1.cnf
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
添加VIP到主库
ifconfig eth0:1 10.0.0.55/24
VIP管理
确保VIP只在主库上存在,故障切换时会自动迁移到新主库。
故障告警配置
准备告警脚本
vim /usr/local/bin/send_report
my $smtp='smtp.qq.com';
my $mail_from='526195417@qq.com';
my $mail_user='526195417';
my $mail_pass='njwygmkbvzlubiji';
my $mail_to='526195417@qq.com';
更新告警配置
vim /etc/mha/app1.cnf
report_script=/usr/local/bin/send_report
重启服务
masterha_stop --conf=/etc/mha/app1.cnf
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
Binlog Server数据补偿
创建目录
mkdir -p /data/binlog_server/
chown -R mysql.mysql /data/*
cd /data/binlog_server/
拉取binlog日志
注意:拉取日志的起点,需要按照目前从库的已经获取到的二进制日志点为起点
mysql -e "show slave status \G"|grep "Master_Log"
mysqlbinlog -R --host=10.0.0.51 --user=mha --password=mha --raw --stop-never mysql-bin.000003 &
ll /data/binlog_server
配置Binlog Server
vim /etc/mha/app1.cnf
[binlog1]
no_master=1
hostname=10.0.0.53
master_binlog_dir=/data/binlog_server/
重启服务
masterha_stop --conf=/etc/mha/app1.cnf
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
Binlog Server的作用
当主库宕机时,Binlog Server保存的日志可以用于补偿各从库间的数据差异,确保数据一致性。
故障演练与恢复
1.停掉主库
2.查看VIP是否会漂移
3.查看从库是否升为新的主库
4.查看从库是否切换为新的复制关系
注意事项
故障演练前请确保已做好数据备份,避免数据丢失。
故障恢复步骤
启动原主库
# db-51启动数据库
systemctl start mysqld
修复主从关系
change master to
master_host='10.0.0.52',
master_user='repl',
master_password='123' ,
MASTER_AUTO_POSITION=1;
start slave;
show slave status\G
检查VIP位置
# db-52确认是否存在VIP
ip a|grep 10.0.0.55
修复Binlog Server
ps -ef|grep mysqlbinlog
rm -rf /data/binlog_server/*
cd /data/binlog_server/
mysql -e "show slave status \G"|grep "Master_Log"
mysqlbinlog -R --host=10.0.0.51 --user=mha --password=mha --raw --stop-never mysql-bin.000003 &
更新节点配置
# 添加新节点到配置文件
masterha_conf_host --command=add --conf=/etc/mha/app1.cnf --hostname=10.0.0.51 --block=server1 --params="port=3306"
# ---------------------删除命令------------------
masterha_conf_host --command=delete --conf=/etc/mha/app1.cnf --block=server1
检查环境
masterha_check_ssh --conf=/etc/mha/app1.cnf
masterha_check_repl --conf=/etc/mha/app1.cnf
启动MHA Manager
nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
验证MHA状态
masterha_check_status --conf=/etc/mha/app1.cnf
在线切换主库
在线切换功能
在线切换时,自动锁原主库,VIP自动切换
配置切换脚本
vim /usr/local/bin/master_ip_online_change
my $vip = "10.0.0.55/24";
my $key = "1";
my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig eth0:$key $vip down";
my $ssh_Bcast_arp= "/sbin/arping -I eth0 -c 3 -A 10.0.0.55";
更新配置文件
vim /etc/mha/app1.cnf
master_ip_online_change_script=/usr/local/bin/master_ip_online_change
停止MHA
masterha_stop --conf=/etc/mha/app1.cnf
检查复制状态
masterha_check_repl --conf=/etc/mha/app1.cnf
执行在线切换
masterha_master_switch --conf=/etc/mha/app1.cnf --master_state=alive --new_master_host=10.0.0.51 --orig_master_is_new_slave --running_updates_limit=10000
重构Binlog Server
[root@db-53 ~]# ps -ef |grep [m]ysqlbinlog
root 33698 23489 0 21:18 pts/0 00:00:00 mysqlbinlog -R --host=10.0.0.52 --user=mha --password=x x --raw --stop-never mysql-bin.000003
[root@db-53 ~]# kill 33698
[root@db-53 ~]# cd /data/binlog_server/
[root@db-53 binlog_server]# rm -rf *
[root@db-53 binlog_server]# mysqlbinlog -R --host=10.0.0.51 --user=mha --password=mha --raw --stop-never mysql-bin.000003 &
[1] 36893
启动MHA服务
[root@db-53 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 &
[2] 36915
[root@db-53 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:36915) is running(0:PING_OK), master:10.0.0.51
在线切换注意事项
- 在线切换前确保没有大事务在执行
--running_updates_limit
参数控制等待运行中事务的时间- 切换后需要重新配置Binlog Server指向新主库
更新: 2025-01-07 08:54:21