MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司的youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
该软件由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。
在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制,可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。
目前MHA主要支持一主多从的架构。要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库。因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝TMHA已经支持一主一从。
1、MHA工作原理总结为以下几条:
2、MHA提供了如下功能:
(1)master自动监控,故障转移一体化(Automated master monitoring and failover)
(2)MHA可以在一个复制组中监控master的状态,如果挂了,就可以自动的做failover。
(3)MHA通过所有slave的差异relay-log来保证数据的一致性。
(4)MHA在做故障转移,日志补偿这些动作的时候,通常只需要10~30秒。
(5)通常情况下,MHA会选择最新的slave作为new master,但是你也可以指定哪些是候选maser,那么新master选举的时候,就从这些host里面挑。
(6)导致复制环境中断的一致性问题,在MHA中是不会发生的,请放心使用。
在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5及以上版本的半同步复制,可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。
(7)手工-交互式master故障转移(Interactive manually initiated Master Failover)
MHA可以配置成手工-交互式方式进行故障转移,不支持监控master的状态。
(8)非交互式master故障转移 (Non-interactive master failover)
非交互式,自动的故障转移,不提供监控master状态功能,监控可以交给其他组件做(如:Pacemaker heartbeat)。
(9)在线master切换 (Online switching master to a different host)
Manager工具包主要包括以下几个工具:
(1)masterha_check_ssh #检查MHA的SSH配置状况;
(2)masterha_check_repl #检查MySQL复制状况;
(3)masterha_manger #启动MHA;
(4)masterha_check_status #检测当前MHA运行状态;
(5)masterha_master_monitor #检测master是否宕机;
(6)masterha_master_switch #控制故障转移(自动或者手动);
(7)masterha_conf_host #添加或删除配置的server信息;
Node工具包(这些工具通常由MHA Manager的脚本触发,无需人为操作)主要包括以下几个工具:
(1)save_binary_logs #保存和复制master的二进制日志;
(2)apply_diff_relay_logs #识别差异的中继日志事件并将其差异的事件应用于其他的slave;
(3)purge_relay_logs #清除中继日志(不会阻塞SQL线程);
注意: 为了尽可能的减少主库硬件损坏宕机造成的数据丢失,因此在配置MHA的同时建议配置成MySQL半同步复制
master | 172.25.75.1 | mysql |
---|---|---|
slave | 172.25.75.2 | mysql |
slave | 172.25.75.3 | mysql |
MHA | 172.25.75.4 | mha |
关闭selinux以及firewalld。做好gtid的主从复制:一主两从
有关gtid的主从复制配置,大家可以参考我之前的文章:Mysql的主从复制以及基于gtid的主从复制、半同步复制、组复制
不同之处在于server1、server2、server3都有可能成为master,故/etc/my.cnf的配置文件:
server1:
server2和server3把server-id改成2和3就好,其他不变。
[root@server4 ~]# ls
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
mha4mysql-node-0.58-0.el7.centos.noarch.rpm perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
[root@server4 ~]# yum install -y mha4mysql-manager-0.58-0.el7.centos.noarch.rpm perl-* mha4mysql-node-0.58-0.el7.centos.noarch.rpm
[root@server4 ~]# ssh-keygen ##先生成密钥
[root@server4 ~]# ssh-copy-id server1:
[root@server4 ~]# ssh-copy-id server2: #发送密钥
[root@server4 ~]# ssh-copy-id server3:
server1 2 3互相之间也要免密:
[root@server4 ~]# scp -r .ssh server1:
[root@server4 ~]# scp -r .ssh server2:
[root@server4 ~]# scp -r .ssh server3:
我们以server1为例,server2、server3作同样的操作:
[root@server1 ~]# yum install -y mha4mysql-node-0.58-0.el7.centos.noarch.rpm
[root@server4 ~]# mkdir /etc/masterha
[root@server4 ~]# vim /etc/masterha/app1.cnf
[server default]
manager_workdir=/etc/masterha
manager_log=/var/log/masterha.log #manager 日志文件
master_binlog_dir=/etc/masterha
#master_ip_failover_script= /usr/local/bin/master_ip_failover
#master_ip_online_change_script= /usr/local/bin/master_ip_online_change
password=Westos+001 #MySQL管理帐号和密码
user=root
ping_interval=1
remote_workdir=/tmp
repl_password=Westos+001 #复制帐号和密码
repl_user=repl
#report_script=/usr/local/send_report
#secondary_check_script= /usr/local/bin/masterha_secondary_check -s server03 -s server02
#shutdown_script=""
ssh_user=root # 系统ssh用户
[server1]
hostname=172.25.75.1
port=3306
[server2]
hostname=172.25.75.2
port=3306
candidate_master=1
check_repl_delay=0
[server3]
hostname=172.25.75.3
port=3306
no_master=1 ##no_master表示这个节点不能作为master
[root@server4 masterha]# masterha_check_ssh --conf=/etc/masterha/app1.cnf
[root@server4 masterha]# masterha_check_repl --conf=/etc/masterha/app1.cnf
Fri Mar 22 10:03:45 2019 - [error][/usr/share/masterha_check_ssh --conf=/etc/masterha/app1.cnfperl5/vendor_perl/MHA/Server.pm, ln180] Got MySQL error when connecting 172.25.0.2(172.25.0.2:3306) :1130:Host 'server4' is not allowed to connect to this MySQL server, but this is not a MySQL crash. Check MySQL server settings.
因为server4默认是用root远程连接数据库,但是在数据库初始化时已经禁用了root的远程连接。
解决方法:主库上授权用户
server1:
mysql> grant all on *.* to root@'%' identified by 'Westos+001'; # 创建监控root,给监控用户授权
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
问题是:没有设置server2和server3两台slave服务器设置read_only=1,不能写入配置文件,因为都有可能提升为master。
解决办法:
在server2和server3分别执行
再次测试:
[root@server4 masterha]# masterha_check_repl --conf=/etc/masterha/app1.cnf
首选,关闭server1的mysqld服务
[root@server1 mysql]# systemctl stop mysqld
在server4上手动切换:
[root@server4 masterha]# masterha_master_switch --master_state=dead --conf=/etc/masterha/app1.cnf --dead_master_host=172.25.75.1 --dead_master_ip=172.25.75.1 --dead_master_port=3306 --new_master_host=172.25.75.2 --new_master_port=3306
'手动切换之前,需要保证主从同步正常,repl复制用户能够远程连接'
中间都选yes
切换成功后,可以在server3上看到它的master已经变成了server2
而server2已经时master了
在server3上查看集群:
master现在为server2,手动切换成功!!!
恢复环境:
手动开启server1的mysql服务,作为slave加入集群。
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> CHANGE MASTER TO
-> MASTER_HOST = '172.25.75.2',
-> MASTER_USER = 'repl',
-> MASTER_PASSWORD = 'Westos+001',
-> MASTER_AUTO_POSITION = 1;
Query OK, 0 rows affected, 2 warnings (0.19 sec)
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)
mysql> show slave status\G;
server4:
[root@server4 masterha]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=172.25.75.1 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
Fri May 10 18:48:21 2019 - [info] MHA::MasterRotate version 0.58.
Fri May 10 18:48:21 2019 - [info] Starting online master switch..
Fri May 10 18:48:21 2019 - [info]
Fri May 10 18:48:21 2019 - [info] * Phase 1: Configuration Check Phase..
Fri May 10 18:48:21 2019 - [info]
Fri May 10 18:48:21 2019 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Fri May 10 18:48:21 2019 - [info] Reading application default configuration from /etc/masterha/app1.cnf..
Fri May 10 18:48:21 2019 - [info] Reading server configuration from /etc/masterha/app1.cnf..
Fri May 10 18:48:22 2019 - [info] GTID failover mode = 1
Fri May 10 18:48:22 2019 - [info] Current Alive Master: 172.25.75.2(172.25.75.2:3306)
Fri May 10 18:48:22 2019 - [info] Alive Slaves:
Fri May 10 18:48:22 2019 - [info] 172.25.75.1(172.25.75.1:3306) Version=5.7.24-log (oldest major version between slaves) log-bin:enabled
Fri May 10 18:48:22 2019 - [info] GTID ON
Fri May 10 18:48:22 2019 - [info] Replicating from 172.25.75.2(172.25.75.2:3306)
Fri May 10 18:48:22 2019 - [info] 172.25.75.3(172.25.75.3:3306) Version=5.7.24-log (oldest major version between slaves) log-bin:enabled
Fri May 10 18:48:22 2019 - [info] GTID ON
Fri May 10 18:48:22 2019 - [info] Replicating from 172.25.75.2(172.25.75.2:3306)
Fri May 10 18:48:22 2019 - [info] Not candidate for the new Master (no_master is set)
It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 172.25.75.2(172.25.75.2:3306)? (YES/no): yes #需要在新的master上执行命令
Fri May 10 18:48:24 2019 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
Fri May 10 18:48:24 2019 - [info] ok.
Fri May 10 18:48:24 2019 - [info] Checking MHA is not monitoring or doing failover..
Fri May 10 18:48:24 2019 - [info] Checking replication health on 172.25.75.1..
Fri May 10 18:48:24 2019 - [info] ok.
Fri May 10 18:48:24 2019 - [info] Checking replication health on 172.25.75.3..
Fri May 10 18:48:24 2019 - [info] ok.
Fri May 10 18:48:24 2019 - [info] 172.25.75.1 can be new master.
Fri May 10 18:48:24 2019 - [info]
From:
172.25.75.2(172.25.75.2:3306) (current master)
+--172.25.75.1(172.25.75.1:3306)
+--172.25.75.3(172.25.75.3:3306)
To:
172.25.75.1(172.25.75.1:3306) (new master)
+--172.25.75.3(172.25.75.3:3306)
+--172.25.75.2(172.25.75.2:3306)
Starting master switch from 172.25.75.2(172.25.75.2:3306) to 172.25.75.1(172.25.75.1:3306)? (yes/NO): yes #开始切换master
Fri May 10 18:48:26 2019 - [info] Checking whether 172.25.75.1(172.25.75.1:3306) is ok for the new master..
Fri May 10 18:48:26 2019 - [info] ok.
Fri May 10 18:48:26 2019 - [info] 172.25.75.2(172.25.75.2:3306): SHOW SLAVE STATUS returned empty result. To check replication filtering rules, temporarily executing CHANGE MASTER to a dummy host.
Fri May 10 18:48:26 2019 - [info] 172.25.75.2(172.25.75.2:3306): Resetting slave pointing to the dummy host.
Fri May 10 18:48:26 2019 - [info] ** Phase 1: Configuration Check Phase completed.
Fri May 10 18:48:26 2019 - [info]
Fri May 10 18:48:26 2019 - [info] * Phase 2: Rejecting updates Phase..
Fri May 10 18:48:26 2019 - [info]
master_ip_online_change_script is not defined. If you do not disable writes on the current master manually, applications keep writing on the current master. Is it ok to proceed? (yes/NO): yes
Fri May 10 18:48:28 2019 - [info] Locking all tables on the orig master to reject updates from everybody (including root):
Fri May 10 18:48:28 2019 - [info] Executing FLUSH TABLES WITH READ LOCK..
Fri May 10 18:48:28 2019 - [info] ok.
Fri May 10 18:48:28 2019 - [info] Orig master binlog:pos is mysql-bin.000002:154.
Fri May 10 18:48:28 2019 - [info] Waiting to execute all relay logs on 172.25.75.1(172.25.75.1:3306)..
Fri May 10 18:48:28 2019 - [info] master_pos_wait(mysql-bin.000002:154) completed on 172.25.75.1(172.25.75.1:3306). Executed 0 events.
Fri May 10 18:48:28 2019 - [info] done.
Fri May 10 18:48:28 2019 - [info] Getting new master's binlog name and position..
Fri May 10 18:48:28 2019 - [info] mysql-bin.000003:194
Fri May 10 18:48:28 2019 - [info] All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='172.25.75.1', MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER='repl', MASTER_PASSWORD='xxx';
Fri May 10 18:48:28 2019 - [info] Setting read_only=0 on 172.25.75.1(172.25.75.1:3306)..
Fri May 10 18:48:28 2019 - [info] ok.
Fri May 10 18:48:28 2019 - [info]
Fri May 10 18:48:28 2019 - [info] * Switching slaves in parallel..
Fri May 10 18:48:28 2019 - [info]
Fri May 10 18:48:28 2019 - [info] -- Slave switch on host 172.25.75.3(172.25.75.3:3306) started, pid: 12513
Fri May 10 18:48:28 2019 - [info]
Fri May 10 18:48:29 2019 - [info] Log messages from 172.25.75.3 ...
Fri May 10 18:48:29 2019 - [info]
Fri May 10 18:48:28 2019 - [info] Waiting to execute all relay logs on 172.25.75.3(172.25.75.3:3306)..
Fri May 10 18:48:28 2019 - [info] master_pos_wait(mysql-bin.000002:154) completed on 172.25.75.3(172.25.75.3:3306). Executed 0 events.
Fri May 10 18:48:28 2019 - [info] done.
Fri May 10 18:48:28 2019 - [info] Resetting slave 172.25.75.3(172.25.75.3:3306) and starting replication from the new master 172.25.75.1(172.25.75.1:3306)..
Fri May 10 18:48:28 2019 - [info] Executed CHANGE MASTER.
Fri May 10 18:48:28 2019 - [info] Slave started.
Fri May 10 18:48:29 2019 - [info] End of log messages from 172.25.75.3 ...
Fri May 10 18:48:29 2019 - [info]
Fri May 10 18:48:29 2019 - [info] -- Slave switch on host 172.25.75.3(172.25.75.3:3306) succeeded.
Fri May 10 18:48:29 2019 - [info] Unlocking all tables on the orig master:
Fri May 10 18:48:29 2019 - [info] Executing UNLOCK TABLES..
Fri May 10 18:48:29 2019 - [info] ok.
Fri May 10 18:48:29 2019 - [info] Starting orig master as a new slave..
Fri May 10 18:48:29 2019 - [info] Resetting slave 172.25.75.2(172.25.75.2:3306) and starting replication from the new master 172.25.75.1(172.25.75.1:3306)..
Fri May 10 18:48:29 2019 - [info] Executed CHANGE MASTER.
Fri May 10 18:48:29 2019 - [info] Slave started.
Fri May 10 18:48:29 2019 - [info] All new slave servers switched successfully.
Fri May 10 18:48:29 2019 - [info]
Fri May 10 18:48:29 2019 - [info] * Phase 5: New master cleanup phase..
Fri May 10 18:48:29 2019 - [info]
Fri May 10 18:48:29 2019 - [info] 172.25.75.1: Resetting slave info succeeded.
Fri May 10 18:48:29 2019 - [info] Switching master to 172.25.75.1(172.25.75.1:3306) completed successfully.
在server2上查看:
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.25.75.1
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 194
Relay_Log_File: server2-relay-bin.000002
Relay_Log_Pos: 367
Relay_Master_Log_File: mysql-bin.000003
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
因为用户访问入口只能有一个,所以需要配置vip
[root@server4 ~]# ls
mha4mysql-manager-0.58-0.el7.centos.noarch.rpm perl-Mail-Sender-0.8.23-1.el7.noarch.rpm
mha4mysql-manager-0.58.tar.gz perl-Mail-Sendmail-0.79-21.el7.noarch.rpm
mha4mysql-node-0.58-0.el7.centos.noarch.rpm perl-MIME-Lite-3.030-1.el7.noarch.rpm
perl-Config-Tiny-2.14-7.el7.noarch.rpm perl-MIME-Types-1.38-2.el7.noarch.rpm
perl-Email-Date-Format-1.002-15.el7.noarch.rpm perl-Parallel-ForkManager-1.18-2.el7.noarch.rpm
perl-Log-Dispatch-2.41-1.el7.1.noarch.rpm
[root@server4 ~]# tar zxf mha4mysql-manager-0.58.tar.gz
[root@server4 ~]# cd mha4mysql-manager-0.58
[root@server4 mha4mysql-manager-0.58]# ls
AUTHORS bin COPYING debian lib Makefile.PL MANIFEST MANIFEST.SKIP README rpm samples t tests
[root@server4 mha4mysql-manager-0.58]# cd bin/
[root@server4 bin]# ls
masterha_check_repl masterha_check_status masterha_manager masterha_master_switch masterha_stop
masterha_check_ssh masterha_conf_host masterha_master_monitor masterha_secondary_check
[root@server4 scripts]# pwd
/root/mha4mysql-manager-0.58/samples/scripts
[root@server4 scripts]# ls
master_ip_failover master_ip_online_change power_manager send_report
[root@server4 scripts]# vim master_ip_failover
11 my $vip = '172.25.75.100/24';
12 my $key = '1';
13 my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";
14 my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";
[root@server4 scripts]# vim master_ip_online_change//
7 my $vip = '172.25.75.100/24'; # Virtual IP
8 my $key = "1";
9 my $ssh_start_vip = "/sbin/ip addr add $vip dev eth0";
10 my $ssh_stop_vip = "/sbin/ip addr del $vip dev eth0";
11 my $exit_code = 0;
将两个配置文件,复制到/usr/local/bin,添加执行权限
[root@server4 ~]# cp master_ip_failover master_ip_online_change /usr/local/bin/
[root@server4 ~]# cp master_ip_failover master_ip_online_change /usr/local/bin/
[root@server4 ~]# chmod +x /usr/local/bin/master_ip_*
[root@server4 ~]# ll /usr/local/bin/master_ip_*
-rwxr-xr-x 1 root root 3802 May 10 19:13 /usr/local/bin/master_ip_failover
-rwxr-xr-x 1 root root 10024 May 10 19:13 /usr/local/bin/master_ip_online_change
5 master_ip_failover_script= /usr/local/bin/master_ip_failover
6 master_ip_online_change_script= /usr/local/bin/master_ip_online_change
[root@server1 mysql]# ip addr add 172.25.75.100/24 dev eth0
[root@server1 mysql]# ip addr show
1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 52:54:00:3b:1a:11 brd ff:ff:ff:ff:ff:ff
inet 172.25.75.1/24 brd 172.25.75.255 scope global eth0
valid_lft forever preferred_lft forever
inet 172.25.75.100/24 scope global secondary eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe3b:1a11/64 scope link
valid_lft forever preferred_lft forever
server4:
[root@server4 ~]# masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=172.25.75.2 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
在server4上先删除app1.failover.complete
rm -rf /etc/masterha/app1.failover.complete
开启manager
[root@server4 masterha]# nohup masterha_manager --conf=/etc/masterha/app1.cnf &>/dev/null &
[1] 12672
[root@server4 masterha]# ps ax
12672 pts/0 S 0:00 perl /usr/bin/masterha_manager --conf=/etc/masterha/app1.cnf
12688 pts/0 R+ 0:00 ps ax
然后在master(server2)上关闭mysql,模拟故障
[root@server2 mysql]# systemctl stop mysqld
在server4上查看日志
[root@server4 masterha]#
[1]+ Done nohup masterha_manager --conf=/etc/masterha/app1.cnf &>/dev/null
[root@server4 masterha]# cat /var/log/masterha.log