MySQL高可用架构——MHA
一、认识MHA
1.1 MHA概述
MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。 MHA里有两个角色一个是MHA Node(数据节点)另一个是MHA Manager(管理节点)。 MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。
MHA工作架构示意图如下:
大概流程如下:
1、从宕机的master保存二进制日志事件;
2、识别含有最新更新的slave;
3、应用差异的中继日志(relay log)到其他slave;
4、应用从master保存的二进制日志事件;
5、提升一个slave为新master;
6、使其他的slave连接到新的master进行复制;
1.2 MHA架构工作原理
主库宕机处理过程:
1)监控节点 (通过配置文件获取所有节点信息)
① 系统,网络,SSH连接性;
② 主从状态,重点是主库;
2)选主
① 如果判断从库(position或者GTID),数据有差异,最接近于Master的slave,成为备选主;
② 如果判断从库(position或者GTID),数据一致,按照配置文件顺序,选主;
③ 如果设定有权重(candidate_master=1),按照权重强制指定备选主;
1)默认情况下如果一个slave落后master 100M的relay logs的话,即使有权重,也会失效;
2)如果check_repl_delay=0的化,即使落后很多日志,也强制选择其为备选主;
3)数据补偿
① 当SSH能连接,从库对比主库GTID 或者position号,立即将二进制日志保存至各个从节点并且应用(save_binary_logs );
② 当SSH不能连接, 对比从库之间的relaylog的差异(apply_diff_relay_logs) ;
4)Failover
① 将备选主进行身份切换,对外提供服务;
② 其余从库和新主库确认新的主从关系;
5)应用透明(VIP);
6)二次数据补偿(binlog_server);
1.3 MHA软件组成
Manager工具包主要包括以下几个工具:
masterha_manger 启动MHA
masterha_check_ssh 检查MHA的SSH配置状况
masterha_check_repl 检查MySQL复制状况
masterha_master_monitor 检测master是否宕机
masterha_check_status 检测当前MHA运行状态
masterha_master_switch 控制故障转移(自动或者手动)
masterha_conf_host 添加或删除配置的server信息
Node工具包主要包括以下几个工具:
这些工具通常由MHA Manager的脚本触发,无需人为操作
save_binary_logs 保存和复制master的二进制日志
apply_diff_relay_logs 识别差异的中继日志事件并将其差异的事件应用于其他的
purge_relay_logs 清除中继日志(不会阻塞SQL线程)
1.4 MHA特性
1、MHA切换不依赖实例使用存储引擎和BINLOG格式;
2、MHA不会增加MySQL服务器性能开销,除MHA管理节点外无需增加额外服务器;
3、在MySQL服务器上部署MHA数据节点不会影响当前实例运行;
4、MHA实现自动故障切换,也可以手动触发在线切换;
5、MHA可以实现秒级的故障切换;
6、MHA可以将任意slave提升master,也可以在切换时指定master候选节点;
7、MHA提供扩展接口,允许在MHA切换过程中的特定时间点执行用户自定义脚本。
二、准备环境
2.1 案例环境
OS | hostname | IP | service | type |
---|---|---|---|---|
centos 7.5 | master | 192.168.1.1 | mysql 5.7 | master |
centos 7.5 | backup | 192.168.1.2 | mysql 5.7 | backup |
centos 7.5 | slave | 192.168.1.3 | mysql 5.7 | slave、manager |
2.2 配置节点互信
① 配置hosts文件
只在master节点上完成即可!
[root@master ~]# cat >> /etc/hosts << EOF
> 192.168.1.1 master
> 192.168.1.2 backup
> 192.168.1.3 slave
> EOF
[root@master ~]# for i in master backup slave;do scp /etc/hosts $i:/etc/;done
② 配置免密登录
以下操作需在所有节点上完成!
[root@master ~]# ssh-keygen -t rsa
[root@master ~]# for i in master backup slave;do ssh-copy-id $i;done
所有节点配置完成后,使用以下命令进行检测:
[root@master ~]# for i in master backup slave;do ssh $i hostname;done
master
backup
slave
#无需输入密码,即可显示正确的结果
三、配置一主两从(GTID方式)
3.1 清理环境
所有节点都需进行以下操作!
[root@master ~]# systemctl stop mysqld
[root@master ~]# rm -rf /usr/local/mysql/data/*
#使用GTID同步的方式必须保证数据库是停止的状态,并且删除数据库数据目录下的所有东西
3.2 准备数据库配置文件
master节点:
[root@master ~]# cat > /etc/my.cnf << EOF
[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/usr/local/mysql/mysql.sock
server_id=1
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/usr/local/mysql/data/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=master[\\d]>
EOF
backup节点:
[root@backup ~]# cat > /etc/my.cnf << EOF
[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/usr/local/mysql/mysql.sock
server_id=2
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/usr/local/mysql/data/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=backup[\\d]>
EOF
slave节点:
[root@slave ~]# cat > /etc/my.cnf << EOF
[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/usr/local/mysql/mysql.sock
server_id=3
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/usr/local/mysql/data/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=slave[\\d]>
EOF
3.3 初始化数据库、启动数据库
所有节点都需进行以下操作:
[root@master ~]# mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
[root@master ~]# systemctl start mysqld
3.4 构建主从关系
master节点:
[root@master ~]# mysql
master[(none)]>grant replication slave on *.* to repl@'192.168.1.%' identified by '123';
#主库创建专用于主从复制的用户
backup节点:
[root@backup ~]# mysql
backup[(none)]>change master to
master_host='192.168.1.1',
master_user='repl',
master_password='123',
master_auto_position=1;
backup[(none)]>start slave;
slave节点:
[root@slave ~]# mysql
slave[(none)]>change master to
master_host='192.168.1.1',
master_user='repl',
master_password='123',
master_auto_position=1;
slave[(none)]>start slave;
3.5 验证主从复制
master[(none)]>show master status;
+------------------+----------+--------------+------------------+----------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+----------------------------------------+
| mysql-bin.000002 | 447 | | | e6ff02dc-81fd-11ea-a229-000c29667213:1 |
+------------------+----------+--------------+------------------+----------------------------------------+
#查看主库信息,注意GTID号
backup[(none)]>show slave status \G
Retrieved_Gtid_Set: e6ff02dc-81fd-11ea-a229-000c29667213:1
Executed_Gtid_Set: e6ff02dc-81fd-11ea-a229-000c29667213:1
slave[(none)]>show slave status \G
Retrieved_Gtid_Set: e6ff02dc-81fd-11ea-a229-000c29667213:1
Executed_Gtid_Set: e6ff02dc-81fd-11ea-a229-000c29667213:1
#从库检测与主库的GTID号一致
四、搭建简易——MHA
4.1 配置关键程序软连接
以下操作所有节点必须都进行操作!
[root@master ~]# ln -s /usr/local/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
[root@master ~]# ln -s /usr/local/mysql/bin/mysql /usr/bin/mysql
4.2 安装MHA
① 获取MHA软件
mha官网:
https://code.google.com/archive/p/mysql-master-ha/
github下载地址:
https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads
也可使用文中的地址进行下载
② 所有节点安装node软件及其依赖
[root@master ~]# yum -y install perl-DBD-MySQL
[root@master ~]# wget https://github.com/yoshinorim/mha4mysql-node/releases/download/v0.58/mha4mysql-node-0.58-0.el7.centos.noarch.rpm
[root@master ~]# rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm
③ 在master主库上创建MHA需要的数据库用户
master[(none)]>grant all privileges on *.* to mha@'192.168.1.%' identified by 'mha';
④ slave节点安装manager软件及其依赖
[root@slave ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@slave ~]# yum makecache
[root@slave ~]# yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
[root@slave ~]# wget https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
[root@slave ~]# rpm -ivh mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
4.3 slave节点准备MHA的配置文件
[root@slave ~]# mkdir -p /etc/mha
#创建配置文件目录
[root@slave ~]# mkdir -p /var/log/mha/app1
#创建日志文件目录
[root@slave ~]# vim /etc/mha/app1.cnf
#编写MHA的配置文件
[server default]
manager_log=/var/log/mha/app1/manager #MHA日志文件
manager_workdir=/var/log/mha/app1 #MHA工作目录
master_binlog_dir=/usr/local/mysql/data/ #主库的binlog日志位置
user=mha
password=mha #MHA专用用户
ping_interval=2 #监控间隔(秒数)每两秒监控一次,监控两次
repl_password=123
repl_user=repl #主从复制专用用户
ssh_user=root #配置互信用户名
[server1]
hostname=192.168.1.1
port=3306
[server2]
hostname=192.168.1.2
port=3306
[server3]
hostname=192.168.1.3
port=3306
#注意配置文件中不允许有注释内容
4.4 slave节点监测MHA状态
① 互信检测
[root@slave ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
② 主从状态监测
[root@slave ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
4.5 slave节点启动MHA
[root@slave ~]# 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 &
#如果有多个MySQL主从复制实例可以启动多次,当然需要指定不同的配置文件
4.6 slave节点监测MHA的状态
[root@slave ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:3123) is running(0:PING_OK), master:192.168.1.1
至此一个简单的MHA架构已经完成!
五、完善MHA——添加VIP
5.1 slave节点准备脚本文件
[root@slave ~]# vim /usr/local/bin/master_ip_failover
#!/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
);
my $vip = '192.168.1.100'; #指定虚拟IP地址
my $key = '0'; #指定网卡编号
my $ssh_start_vip = "/sbin/ifconfig ens33:$key $vip"; #启动VIP
my $ssh_stop_vip = "/sbin/ifconfig ens33:$key down"; #停止VIP
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" ) {
my $exit_code = 1;
eval {
print "Disabling the VIP on old master: $orig_master_host \n";
&stop_vip();
$exit_code = 0;
};
if ($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "start" ) {
my $exit_code = 10;
eval {
print "Enabling the VIP - $vip on the new master - $new_master_host
\n";
&start_vip();
$exit_code = 0;
};
if ($@) {
warn $@;
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "status" ) {
print "Checking the Status of the script.. OK \n";
#`ssh $ssh_user\@cluster1 \" $ssh_start_vip \"`;
exit 0;
}
else {
&usage();
exit 1;
}
}
# A simple system call that enable the VIP on the new master
sub start_vip() {
`ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
# A simple system call that disable the VIP on the old_master
sub stop_vip() {
return 0 unless ($ssh_user);
`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";
}
[root@slave ~]# chmod +x /usr/local/bin/master_ip_failover
5.2 master节点配置虚拟IP
[root@master ~]# ifconfig ens33:1 192.168.1.100/24
5.3 slave节点添加配置参数
[root@slave ~]# vim /etc/mha/app1.cnf
master_ip_failover_script=/usr/local/bin/master_ip_failover
注意:/usr/local/bin/master_ip_failover,必须事先准备好
5.4 重启MHA,查看状态
[root@slave ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@slave ~]# 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 &
[root@slave ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:3625) is running(0:PING_OK), master:192.168.1.1
可以正常查看MHA的状态,表示没有问题!
注意:
- MHA自带的VIP功能,只能在同机房,不可跨机房、网络,如果需要跨机房、网络应该使用Keepalived实现;
- 配置Keepalived时注意优先级切换的问题;
- 配置Keepalived的情况MHA需要添加一些额外参数,否则切换VIP时,可能会出现意想不到的错误;
主库宕机谁来接管?
1. 所有从节点日志都是一致的,默认会以配置文件的顺序去选择一个新主。
2. 从节点日志不一致,自动选择最接近于主库的从库
3. 如果对于某节点设定了权重(candidate_master=1),权重节点会优先选择。
但是此节点日志量落后主库100M日志的话,也不会被选择。可以配合check_repl_delay=0,关闭日志量的检查,强制选择候选节点。
(1) ping_interval=1
#设置监控主库,发送ping包的时间间隔,尝试三次没有回应的时候自动进行failover
(2) candidate_master=1
#设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave
(3)check_repl_delay=0
#默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master;
六、完善MHA——数据补偿binlogserver
binlogserver配置:找一台额外的机器,必须要有5.6以上的版本,支持gtid并开启,我们直接使用用的slave节点!
6.1 slave节点配置MHA配置文件
[root@slave ~]# vim /etc/mha/app1.cnf
[binlog1]
no_master=1 #不参与选主
hostname=192.168.1.3
master_binlog_dir=/data/mysql/binlog
6.2 slave节点创建必要的目录
[root@slave ~]# mkdir -p /data/mysql/binlog
[root@slave ~]# chown -R mysql.mysql /data/*
6.3 节点拉取主库的binlog日志
[root@slave ~]# cd /data/mysql/binlog
#必须进入指定的目录进行拉取日志信息
[root@slave binlog]# mysqlbinlog -R --host=192.168.1.1 --user=mha --password=mha --raw --stop-never mysql-bin.000001 &
#指定主库IP地址使用的用户、密码,从不停止的拉取、指定起始的日志文件,放到后台运行
注意:拉取日志的起点,需要按照目前从库的已经获取到的二进制日志点为起点!
6.4 slave节点重启MHA
[root@slave ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@slave ~]# 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 &
[root@slave ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:4147) is running(0:PING_OK), master:192.168.1.1
七、测试MHA
7.1 master节点模拟故障
[root@master ~]# systemctl stop mysqld
7.2 slave节点排查manager进程
[root@slave ~]# ps -ef | grep manager
root 4472 2705 0 14:32 pts/0 00:00:00 grep --color=auto manager
#manager进程已经退出
[root@slave ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
#MHA已经停止工作
7.3 backup节点查看VIP
[root@backup ~]# ip a
7.4 slave节点查看MHA状态
[root@slave ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
#MHA已经停止工作
7.5 slave节点查看切换日志
[root@slave ~]# tail /var/log/mha/app1/manager
7.6 slave节点查看MHA配置文件
[root@slave ~]# cat /etc/mha/app1.cnf
如果节点已经消失,说明切换过程是成功的;
如果节点还存在,,证明切换过程中出现问题;
7.7 slave节点确认当前主从关系
[root@slave ~]# mysql
slave[(none)]>show slave status \G
八、修复MHA故障
8.1 修复故障库
[root@master ~]# systemctl start mysqld
8.2 故障库修复主从关系
[root@master ~]# mysql
master[(none)]>change master to
master_host='192.168.1.2',
master_user='repl',
master_password='123',
master_auto_position=1;
master[(none)]>start slave;
8.3 slave节点修复配置文件
[root@slave ~]# vim /etc/mha/app1.cnf
#添加以下内容
[server1]
hostname=192.168.1.1
port=3306
8.4 slave节点检查ssh互信和repl的主从关系
[root@slave ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
[root@slave ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
8.5 slave节点修复binlogserver
主节点查看二进制日志
backup[(none)]>show master status;
+------------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+------------------------------------------+
| mysql-bin.000002 | 736 | | | e6ff02dc-81fd-11ea-a229-000c29667213:1-2 |
+------------------+----------+--------------+------------------+------------------------------------------+
#查看当前二进制日志
[root@slave ~]# cd /data/mysql/binlog/
[root@slave binlog]# rm -rf *
[root@slave binlog]# mysqlbinlog -R --host=192.168.1.2 --user=mha --password=mha --raw --stop-never mysql-bin.000002 &
#查看主库当前日志进行拉取
检查VIP,如果切换过程中VIP发生故障,手工生成!
8.6 slave节点启动MHA
[root@slave ~]# 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 &
[root@slave ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:63142) is running(0:PING_OK), master:192.168.1.2