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工作架构示意图如下:
MySQL高可用架构——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 

MySQL高可用架构——MHA

② 主从状态监测
[root@slave ~]# masterha_check_repl  --conf=/etc/mha/app1.cnf

MySQL高可用架构——MHA

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,必须事先准备好

MySQL高可用架构——MHA

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

MySQL高可用架构——MHA

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

MySQL高可用架构——MHA

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

MySQL高可用架构——MHA

7.6 slave节点查看MHA配置文件

[root@slave ~]# cat /etc/mha/app1.cnf

MySQL高可用架构——MHA

如果节点已经消失,说明切换过程是成功的;
如果节点还存在,,证明切换过程中出现问题;

7.7 slave节点确认当前主从关系

[root@slave ~]# mysql
slave[(none)]>show slave status \G

MySQL高可用架构——MHA

八、修复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

MySQL高可用架构——MHA

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

MySQL高可用架构——MHA
MySQL高可用架构——MHA

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

MySQL高可用架构——MHA

上一篇:MySQL主从GTID复制


下一篇:分布式事务一--Mysql本地事务和事务隔离级别