目录
一、MHA简介
二、实验架构设计
1. 基本环境
2. 架构设计
三、MHA安装配置
1. 配置主从复制
2. 安装Perl等依赖模块
3. 配置SSH登录无密码验证
4. 安装MHA Node
5. 安装MHA Manager
6. 配置MHA
7. 创建相关脚本
四、检查MHA配置
1. 检查SSH配置
2. 检查整个复制环境状况
3. 检查MHA Manager的状态
4. 查看启动日志
五、功能测试
1. 初始绑定VIP
2. 测试自动切换
3. 测试手工切换
4. 测试在线切换
5. 修复宕机的Master
参考:
MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司的youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
该软件由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。
在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制,可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。
目前MHA主要支持一主多从的架构。要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库。因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝TMHA已经支持一主一从。(出自:《深入浅出MySQL(第二版)》)从代码层面看,MHA就是一套Perl脚本,那么相信以阿里系的技术实力,将MHA改成支持一主一从也并非难事。
图1所示为MHA架构:
MHA工作原理总结为以下几条:
官方介绍:https://code.google.com/archive/p/mysql-master-ha/
角色 |
IP |
主机名 |
网卡 |
server_id |
功能 |
Monitor Host |
172.16.1.124 |
hdp1 |
- |
- |
监控复制组 |
Master |
172.16.1.127 |
hdp4 |
ens160 |
127 |
响应写请求 |
Candidate Master |
172.16.1.126 |
hdp3 |
ens32 |
126 |
响应读请求 |
Slave |
172.16.1.125 |
hdp2 |
ens32 |
125 |
响应读请求 |
表1
实验架构如图2所示。
hdp1作为MHA Manager,其它三台主机构成MySQL一主二从复制集群,作为MHA Node。
MySQL主从复制的配置较为简单,具体过程可参考MySQL官方文档,这里从略。如果是全新搭建的复制,只要打开Master的binlog,然后将Slave change master到指定的file和pos,再start slave即可。如果是为已经存在且正在使用的数据库搭建从库,有两种方式,一是用mysqldump master-data参数记录master的file和pos,但可能卡库;比较好的方法是用innobackupex联机搭建从库,过程如下:
(1)前置条件
yum install perl perl-DBI perl-DBD-MySQL perl-IO-Socket-SSL perl-Time-HiRes
.:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/home/mysql/mysql-5.6.14/bin:/home/mysql/percona-xtrabackup-2.2.4-Linux-x86_64/bin:/home/mysql/bin
(2)配置主到从的SSH免密码连接
在主上用mysql用户执行:
ssh-keygen
... 一路回车 ...
ssh-copy-id slave的IP或主机名
(3)备份并传输
例如,在主上用mysql用户执行:
innobackupex --user root --password 123456 --defaults-file=/home/mysql/mysql-5.6.14/my.cnf --no-lock --socket=/home/mysql/mysql-5.6.14/mysql.sock --port 3306 --stream=tar ./ | ssh [email protected] \ "cat - > /home/mysql/backup.tar"
(4)恢复备份
在从上用mysql用户执行:
# 解压缩
tar -ixvf backup.tar -C /home/mysql/mysql-5.6.14/data
# 应用日志
innobackupex --apply-log /home/mysql/mysql-5.6.14/data/
# 查看binlog日志文件的位置值
cat /home/mysql/mysql-5.6.14/data/xtrabackup_binlog_info
# 编辑my.cnf
vi /etc/my.cnf
# 启动MySQL,目录要和主保持一致
service mysql start
mysql -uroot -p123456 -P3306 -h127.0.0.1
# 配置复制
reset master;
reset slave all;
change master to
master_host='172.16.1.127',
master_port=3306,
master_user='repl',
master_password='123456',
master_log_file='mysql-bin.000001',
master_log_pos=120;
# 其中master_log_file和master_log_pos赋予/home/mysql/mysql5.6.14/data/xtrabackup_binlog_info中的值。
# 启动slave
start slave;
# 查看slave状态
show slave status\G
(5)后续工作
备份my.cnf、bat文件和crontab等。
用root用户在所有四个节点执行下面的操作。
# 安装一个epel源
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo
# 用yum安装依赖包
yum install perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes -y
在hdp1 172.16.1.124(Monitor)上用root用户执行:
ssh-keygen -t rsa
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
在hdp4 172.16.1.127(Master)上用root用户执行:
ssh-keygen -t rsa
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
在hdp3 172.16.1.126(slave1)上用root用户执行:
ssh-keygen -t rsa
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
在hdp2 172.16.1.125(slave2)上用root用户执行:
ssh-keygen -t rsa
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
ssh-copy-id -i /root/.ssh/id_rsa.pub [email protected]
下载地址:https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads
在hdp2、hdp3、hdp4上用root用户执行下面的操作。
rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
安装完成后,在/usr/bin/目录下有如下MHA相关文件:
apply_diff_relay_logs
filter_mysqlbinlog
purge_relay_logs
save_binary_logs
这些脚本工具通常由MHA Manager的脚本触发,无需人为操作。脚本说明:
下载地址:https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads
在hdp1上用root用户执行下面的操作。
rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm
安装完成后,在/usr/bin/目录下有如下MHA相关文件:
masterha_check_repl
masterha_check_ssh
masterha_check_status
masterha_conf_host
masterha_manager
masterha_master_monitor
masterha_master_switch
masterha_secondary_check
masterha_stop
apply_diff_relay_logs
filter_mysqlbinlog
purge_relay_logs
save_binary_logs
在hdp1上用root用户执行下面(1)、(2)、(3)的操作。
(1)建立配置文件目录
mkdir -p /etc/masterha
(2)创建配置文件/etc/masterha/app1.cnf,内容如下:
[server default]
manager_log=/var/log/masterha/app1/manager.log
manager_workdir=/var/log/masterha/app1.log
master_binlog_dir=/data
master_ip_failover_script=/usr/bin/master_ip_failover
master_ip_online_change_script=/usr/bin/master_ip_online_change
password=123456
ping_interval=1
remote_workdir=/tmp
repl_password=123456
repl_user=repl
secondary_check_script=/usr/bin/masterha_secondary_check -s hdp4 -s hdp3 --user=root --master_host=hdp4 --master_ip=172.16.1.127 --master_port=3306
shutdown_script=""
ssh_user=root
user=root
[server1]
hostname=172.16.1.127
port=3306
[server2]
candidate_master=1
check_repl_delay=0
hostname=172.16.1.126
port=3306
[server3]
hostname=172.16.1.125
port=3306
server default段是manager的一些基本配置参数,server1、server2、server3分别对应复制中的master、第一个slave、第二个slave。该文件的语法要求严格,变量值后不要有多余的空格。主要配置项说明如下。
(3)建立软连接
ln -s /home/mysql/mysql-5.6.14/bin/mysqlbinlog /usr/bin/mysqlbinlog
ln -s /home/mysql/mysql-5.6.14/bin/mysql /usr/bin/mysql
(4)设置复制中Slave的relay_log_purge参数
在hdp3和hdp2上用mysql用户执行:
mysql -uroot -p123456 -e "set global relay_log_purge=0"
注意,MHA在发生切换的过程中,从库的恢复过程中依赖于relay log的相关信息,所以这里要将relay log的自动清除设置为OFF,采用手动清除relay log的方式。默认情况下,从服务器上的中继日志会在SQL线程执行完毕后被自动删除。但是在MHA环境中,这些中继日志在恢复其他从服务器时可能会被用到,因此需要禁用中继日志的自动删除功能。定期清除中继日志需要考虑到复制延时的问题。在ext3的文件系统下,删除大的文件需要一定的时间,会导致严重的复制延时。为了避免复制延时,需要暂时为中继日志创建硬链接,因为在linux系统中通过硬链接删除大文件速度会很快。(在mysql数据库中,删除大表时,通常也采用建立硬链接的方式)
(1)创建定期清理relay脚本
在hdp3、hdp2两台slave上建立/root/purge_relay_log.sh文件,内容如下:
#!/bin/bash
. /home/mysql/.bashrc
user=root
passwd=123456
port=3306
log_dir='/data'
work_dir='/data'
purge='/usr/bin/purge_relay_logs'
if [ ! -d $log_dir ]
then
mkdir $log_dir -p
fi
$purge --user=$user --password=$passwd --disable_relay_log_purge --port=$port --workdir=$work_dir >> $log_dir/purge_relay_logs.log 2>&1
purge_relay_logs的参数说明:
改模式为可执行:
chmod 755 purge_relay_log.sh
手工执行/root/purge_relay_log.sh,在控制台输出:
2018-07-31 12:45:20: purge_relay_logs script started.
Found relay_log.info: /data/relay-log.info
Opening /data/hdp2-relay-bin.000001 ..
Opening /data/hdp2-relay-bin.000002 ..
Executing SET GLOBAL relay_log_purge=1; FLUSH LOGS; sleeping a few seconds so that SQL thread can delete older relay log
files (if it keeps up); SET GLOBAL relay_log_purge=0; .. ok.
2018-07-31 12:45:23: All relay log purging operations succeeded.
添加到crontab中:
0 4 * * * /bin/bash /root/purge_relay_log.sh
(2)创建自动failover脚本
在hdp1上创建/usr/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 = '172.16.1.100'; # Virtual IP
my $key = "1";
my $ssh_start_vip = "/sbin/ifconfig ens32:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens160:$key down";
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" ) {
# $orig_master_host, $orig_master_ip, $orig_master_port are passed.
# If you manage master ip address at global catalog database,
# invalidate orig_master_ip here.
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" ) {
# all arguments are passed.
# If you manage master ip address at global catalog database,
# activate new_master_ip here.
# You can also grant write access (create user, set read_only=0, etc) here.
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\@$orig_master_host \" $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() {
`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";
}
注意脚本中VIP漂移的部分。
(3)创建手动failover脚本
在hdp1上创建/usr/bin/master_ip_online_change文件,内容如下:
#!/usr/bin/env perl
## Note: This is a sample script and is notcomplete. Modify the script based on your environment.
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
use MHA::DBHelper;
use MHA::NodeUtil;
# use Time::HiRes qw( sleep gettimeofdaytv_interval );
use Time::HiRes qw(sleep gettimeofday tv_interval);
use Data::Dumper;
my $_tstart;
my $_running_interval = 0.1;
my (
$command, $orig_master_host, $orig_master_ip,
$orig_master_port, $orig_master_user,
$new_master_host, $new_master_ip, $new_master_port,
$new_master_user,
);
my $vip = '172.16.1.100'; # Virtual IP
my $key = "1";
my $ssh_start_vip = "/sbin/ifconfig ens32:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens160:$key down";
my $ssh_user = "root";
my $new_master_password = "123456";
my $orig_master_password = "123456";
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,
'orig_master_user=s' =>\$orig_master_user,
#'orig_master_password=s' => \$orig_master_password,
'new_master_host=s' =>\$new_master_host,
'new_master_ip=s' =>\$new_master_ip,
'new_master_port=i' =>\$new_master_port,
'new_master_user=s' =>\$new_master_user,
#'new_master_password=s' =>\$new_master_password,
);
exit &main();
sub current_time_us {
my ($sec, $microsec ) = gettimeofday();
my$curdate = localtime($sec);
return $curdate . " " . sprintf( "%06d", $microsec);
}
sub sleep_until {
my$elapsed = tv_interval($_tstart);
if ($_running_interval > $elapsed ) {
sleep( $_running_interval - $elapsed );
}
}
sub get_threads_util {
my$dbh = shift;
my$my_connection_id = shift;
my$running_time_threshold = shift;
my$type = shift;
$running_time_threshold = 0 unless ($running_time_threshold);
$type = 0 unless($type);
my@threads;
my$sth = $dbh->prepare("SHOW PROCESSLIST");
$sth->execute();
while ( my $ref = $sth->fetchrow_hashref() ) {
my$id = $ref->{Id};
my$user = $ref->{User};
my$host = $ref->{Host};
my$command = $ref->{Command};
my$state = $ref->{State};
my$query_time = $ref->{Time};
my$info = $ref->{Info};
$info =~ s/^\s*(.*?)\s*$/$1/ if defined($info);
next if ( $my_connection_id == $id );
next if ( defined($query_time) && $query_time <$running_time_threshold );
next if ( defined($command) && $command eq "Binlog Dump" );
next if ( defined($user) && $user eq "system user" );
next
if ( defined($command)
&& $command eq "Sleep"
&& defined($query_time)
&& $query_time >= 1 );
if( $type >= 1 ) {
next if ( defined($command) && $command eq "Sleep" );
nextif ( defined($command) && $command eq "Connect" );
}
if( $type >= 2 ) {
next if ( defined($info) && $info =~ m/^select/i );
next if ( defined($info) && $info =~ m/^show/i );
}
push @threads, $ref;
}
return @threads;
}
sub main {
if ($command eq "stop" ) {
##Gracefully killing connections on the current master
#1. Set read_only= 1 on the new master
#2. DROP USER so that no app user can establish new connections
#3. Set read_only= 1 on the current master
#4. Kill current queries
#* Any database access failure will result in script die.
my$exit_code = 1;
eval {
## Setting read_only=1 on the new master (to avoid accident)
my $new_master_handler = new MHA::DBHelper();
# args: hostname, port, user, password, raise_error(die_on_error)_or_not
$new_master_handler->connect( $new_master_ip, $new_master_port,
$new_master_user, $new_master_password, 1 );
print current_time_us() . " Set read_only on the new master..";
$new_master_handler->enable_read_only();
if ( $new_master_handler->is_read_only() ) {
print "ok.\n";
}
else {
die "Failed!\n";
}
$new_master_handler->disconnect();
# Connecting to the orig master, die if any database error happens
my $orig_master_handler = new MHA::DBHelper();
$orig_master_handler->connect( $orig_master_ip, $orig_master_port,
$orig_master_user, $orig_master_password, 1 );
## Drop application user so that nobodycan connect. Disabling per-session binlog beforehand
#$orig_master_handler->disable_log_bin_local();
#print current_time_us() . " Drpping app user on the origmaster..\n";
#FIXME_xxx_drop_app_user($orig_master_handler);
## Waiting for N * 100 milliseconds so that current connections can exit
my $time_until_read_only = 15;
$_tstart = [gettimeofday];
my @threads = get_threads_util( $orig_master_handler->{dbh},
$orig_master_handler->{connection_id} );
while ( $time_until_read_only > 0 && $#threads >= 0 ) {
if ( $time_until_read_only % 5 == 0 ) {
printf "%s Waiting all running %d threads aredisconnected.. (max %d milliseconds)\n",
current_time_us(), $#threads + 1, $time_until_read_only * 100;
if ( $#threads < 5 ) {
print Data::Dumper->new( [$_] )->Indent(0)->Terse(1)->Dump ."\n"
foreach (@threads);
}
}
sleep_until();
$_tstart = [gettimeofday];
$time_until_read_only--;
@threads = get_threads_util( $orig_master_handler->{dbh},
$orig_master_handler->{connection_id} );
}
## Setting read_only=1 on the current master so that nobody(exceptSUPER) can write
print current_time_us() . " Set read_only=1 on the orig master..";
$orig_master_handler->enable_read_only();
if ( $orig_master_handler->is_read_only() ) {
print "ok.\n";
}
else {
die "Failed!\n";
}
## Waiting for M * 100 milliseconds so that current update queries cancomplete
my $time_until_kill_threads = 5;
@threads = get_threads_util( $orig_master_handler->{dbh},
$orig_master_handler->{connection_id} );
while ( $time_until_kill_threads > 0 && $#threads >= 0 ) {
if ( $time_until_kill_threads % 5 == 0 ) {
printf "%s Waiting all running %d queries aredisconnected.. (max %d milliseconds)\n",
current_time_us(), $#threads + 1, $time_until_kill_threads * 100;
if ( $#threads < 5 ) {
print Data::Dumper->new( [$_] )->Indent(0)->Terse(1)->Dump ."\n"
foreach (@threads);
}
}
sleep_until();
$_tstart = [gettimeofday];
$time_until_kill_threads--;
@threads = get_threads_util( $orig_master_handler->{dbh},
$orig_master_handler->{connection_id} );
}
print "Disabling the VIPon old master: $orig_master_host \n";
&stop_vip();
## Terminating all threads
print current_time_us() . " Killing all applicationthreads..\n";
$orig_master_handler->kill_threads(@threads) if ( $#threads >= 0);
print current_time_us() . " done.\n";
#$orig_master_handler->enable_log_bin_local();
$orig_master_handler->disconnect();
## After finishing the script, MHA executes FLUSH TABLES WITH READ LOCK
$exit_code = 0;
};
if($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "start" ) {
##Activating master ip on the new master
#1. Create app user with write privileges
#2. Moving backup script if needed
#3. Register new master's ip to the catalog database
# We don't return error even thoughactivating updatable accounts/ip failed so that we don't interrupt slaves'recovery.
# If exit code is 0 or 10, MHA does notabort
my$exit_code = 10;
eval{
my $new_master_handler = new MHA::DBHelper();
# args: hostname, port, user, password, raise_error_or_not
$new_master_handler->connect( $new_master_ip, $new_master_port,
$new_master_user, $new_master_password, 1 );
## Set read_only=0 on the new master
#$new_master_handler->disable_log_bin_local();
print current_time_us() . " Set read_only=0 on the newmaster.\n";
$new_master_handler->disable_read_only();
## Creating an app user on the new master
#print current_time_us() . " Creating app user on the newmaster..\n";
#FIXME_xxx_create_app_user($new_master_handler);
#$new_master_handler->enable_log_bin_local();
$new_master_handler->disconnect();
## Update master ip on the catalog database, etc
print "Enabling the VIP -$vip on the new master - $new_master_host \n";
&start_vip();
$exit_code = 0;
};
if($@) {
warn "Got Error: $@\n";
exit $exit_code;
}
exit $exit_code;
}
elsif ( $command eq "status" ) {
#do nothing
exit 0;
}
else{
&usage();
exit 1;
}
}
# A simple system call that enable the VIPon the new master
sub start_vip() {
`ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
# A simple system call that disable the VIPon the old_master
sub stop_vip() {
`ssh $ssh_user\@$orig_master_host \" $ssh_stop_vip \"`;
}
sub usage {
print
"Usage: master_ip_online_change --command=start|stop|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";
die;
}
注意脚本中VIP漂移的部分。
在hdp1上用root用户操作。
[root@hdp1~]#masterha_check_ssh --conf=/etc/masterha/app1.cnf
Tue Jul 31 12:50:22 2018 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Tue Jul 31 12:50:22 2018 - [info] Reading application default configuration from /etc/masterha/app1.cnf..
Tue Jul 31 12:50:22 2018 - [info] Reading server configuration from /etc/masterha/app1.cnf..
Tue Jul 31 12:50:22 2018 - [info] Starting SSH connection tests..
Tue Jul 31 12:50:23 2018 - [debug]
Tue Jul 31 12:50:22 2018 - [debug] Connecting via SSH from [email protected](172.16.1.127:22) to [email protected](172.16.1.126:22)..
Tue Jul 31 12:50:22 2018 - [debug] ok.
Tue Jul 31 12:50:22 2018 - [debug] Connecting via SSH from [email protected](172.16.1.127:22) to [email protected](172.16.1.125:22)..
Tue Jul 31 12:50:23 2018 - [debug] ok.
Tue Jul 31 12:50:24 2018 - [debug]
Tue Jul 31 12:50:23 2018 - [debug] Connecting via SSH from [email protected](172.16.1.126:22) to [email protected](172.16.1.127:22)..
Tue Jul 31 12:50:23 2018 - [debug] ok.
Tue Jul 31 12:50:23 2018 - [debug] Connecting via SSH from [email protected](172.16.1.126:22) to [email protected](172.16.1.125:22)..
Tue Jul 31 12:50:23 2018 - [debug] ok.
Tue Jul 31 12:50:25 2018 - [debug]
Tue Jul 31 12:50:23 2018 - [debug] Connecting via SSH from [email protected](172.16.1.125:22) to [email protected](172.16.1.127:22)..
Tue Jul 31 12:50:23 2018 - [debug] ok.
Tue Jul 31 12:50:23 2018 - [debug] Connecting via SSH from [email protected](172.16.1.125:22) to [email protected](172.16.1.126:22)..
Tue Jul 31 12:50:24 2018 - [debug] ok.
Tue Jul 31 12:50:25 2018 - [info] All SSH connection tests passed successfully.
[root@hdp1~]#
可以看到各个节点ssh验证都是ok的。
在hdp1上用root用户操作。
[root@hdp1~]#masterha_check_repl --conf=/etc/masterha/app1.cnf
Tue Jul 31 12:52:19 2018 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Tue Jul 31 12:52:19 2018 - [info] Reading application default configuration from /etc/masterha/app1.cnf..
Tue Jul 31 12:52:19 2018 - [info] Reading server configuration from /etc/masterha/app1.cnf..
Tue Jul 31 12:52:19 2018 - [info] MHA::MasterMonitor version 0.56.
Tue Jul 31 12:52:21 2018 - [info] GTID failover mode = 0
Tue Jul 31 12:52:21 2018 - [info] Dead Servers:
Tue Jul 31 12:52:21 2018 - [info] Alive Servers:
Tue Jul 31 12:52:21 2018 - [info] 172.16.1.127(172.16.1.127:3306)
Tue Jul 31 12:52:21 2018 - [info] 172.16.1.126(172.16.1.126:3306)
Tue Jul 31 12:52:21 2018 - [info] 172.16.1.125(172.16.1.125:3306)
Tue Jul 31 12:52:21 2018 - [info] Alive Slaves:
Tue Jul 31 12:52:21 2018 - [info] 172.16.1.126(172.16.1.126:3306) Version=5.6.14-log (oldest major version between slaves) log-bin:enabled
Tue Jul 31 12:52:21 2018 - [info] Replicating from 172.16.1.127(172.16.1.127:3306)
Tue Jul 31 12:52:21 2018 - [info] Primary candidate for the new Master (candidate_master is set)
Tue Jul 31 12:52:21 2018 - [info] 172.16.1.125(172.16.1.125:3306) Version=5.6.14-log (oldest major version between slaves) log-bin:enabled
Tue Jul 31 12:52:21 2018 - [info] Replicating from 172.16.1.127(172.16.1.127:3306)
Tue Jul 31 12:52:21 2018 - [info] Current Alive Master: 172.16.1.127(172.16.1.127:3306)
Tue Jul 31 12:52:21 2018 - [info] Checking slave configurations..
Tue Jul 31 12:52:21 2018 - [info] read_only=1 is not set on slave 172.16.1.126(172.16.1.126:3306).
Tue Jul 31 12:52:21 2018 - [info] Checking replication filtering settings..
Tue Jul 31 12:52:21 2018 - [info] binlog_do_db= , binlog_ignore_db=
Tue Jul 31 12:52:21 2018 - [info] Replication filtering check ok.
Tue Jul 31 12:52:21 2018 - [info] GTID (with auto-pos) is not supported
Tue Jul 31 12:52:21 2018 - [info] Starting SSH connection tests..
Tue Jul 31 12:52:23 2018 - [info] All SSH connection tests passed successfully.
Tue Jul 31 12:52:23 2018 - [info] Checking MHA Node version..
Tue Jul 31 12:52:24 2018 - [info] Version check ok.
Tue Jul 31 12:52:24 2018 - [info] Checking SSH publickey authentication settings on the current master..
Tue Jul 31 12:52:24 2018 - [info] HealthCheck: SSH to 172.16.1.127 is reachable.
Tue Jul 31 12:52:24 2018 - [info] Master MHA Node version is 0.56.
Tue Jul 31 12:52:24 2018 - [info] Checking recovery script configurations on 172.16.1.127(172.16.1.127:3306)..
Tue Jul 31 12:52:24 2018 - [info] Executing command: save_binary_logs --command=test --start_pos=4 --binlog_dir=/data --output_file=/tmp/save_binary_logs_test --manager_version=0.56 --start_file=mysql-bin.000001
Tue Jul 31 12:52:24 2018 - [info] Connecting to [email protected](172.16.1.127:22)..
Creating /tmp if not exists.. ok.
Checking output directory is accessible or not..
ok.
Binlog found at /data, up to mysql-bin.000001
Tue Jul 31 12:52:25 2018 - [info] Binlog setting check done.
Tue Jul 31 12:52:25 2018 - [info] Checking SSH publickey authentication and checking recovery script configurations on all alive slave servers..
Tue Jul 31 12:52:25 2018 - [info] Executing command : apply_diff_relay_logs --command=test --slave_user='root' --slave_host=172.16.1.126 --slave_ip=172.16.1.126 --slave_port=3306 --workdir=/tmp --target_version=5.6.14-log --manager_version=0.56 --relay_log_info=/data/relay-log.info --relay_dir=/data/ --slave_pass=xxx
Tue Jul 31 12:52:25 2018 - [info] Connecting to [email protected](172.16.1.126:22)..
Checking slave recovery environment settings..
Opening /data/relay-log.info ... ok.
Relay log found at /data, up to hdp3-relay-bin.000003
Temporary relay log file is /data/hdp3-relay-bin.000003
Testing mysql connection and privileges..Warning: Using a password on the command line interface can be insecure.
done.
Testing mysqlbinlog output.. done.
Cleaning up test file(s).. done.
Tue Jul 31 12:52:25 2018 - [info] Executing command : apply_diff_relay_logs --command=test --slave_user='root' --slave_host=172.16.1.125 --slave_ip=172.16.1.125 --slave_port=3306 --workdir=/tmp --target_version=5.6.14-log --manager_version=0.56 --relay_log_info=/data/relay-log.info --relay_dir=/data/ --slave_pass=xxx
Tue Jul 31 12:52:25 2018 - [info] Connecting to [email protected](172.16.1.125:22)..
Checking slave recovery environment settings..
Opening /data/relay-log.info ... ok.
Relay log found at /data, up to hdp2-relay-bin.000003
Temporary relay log file is /data/hdp2-relay-bin.000003
Testing mysql connection and privileges..Warning: Using a password on the command line interface can be insecure.
done.
Testing mysqlbinlog output.. done.
Cleaning up test file(s).. done.
Tue Jul 31 12:52:25 2018 - [info] Slaves settings check done.
Tue Jul 31 12:52:25 2018 - [info]
172.16.1.127(172.16.1.127:3306) (current master)
+--172.16.1.126(172.16.1.126:3306)
+--172.16.1.125(172.16.1.125:3306)
Tue Jul 31 12:52:25 2018 - [info] Checking replication health on 172.16.1.126..
Tue Jul 31 12:52:25 2018 - [info] ok.
Tue Jul 31 12:52:25 2018 - [info] Checking replication health on 172.16.1.125..
Tue Jul 31 12:52:25 2018 - [info] ok.
Tue Jul 31 12:52:25 2018 - [info] Checking master_ip_failover_script status:
Tue Jul 31 12:52:25 2018 - [info] /usr/bin/master_ip_failover --command=status --ssh_user=root --orig_master_host=172.16.1.127 --orig_master_ip=172.16.1.127 --orig_master_port=3306
IN SCRIPT TEST====/sbin/ifconfig ens160:1 down==/sbin/ifconfig ens32:1 172.16.1.100===
Checking the Status of the script.. OK
SIOCSIFADDR: No such device
ens32:1: ERROR while getting interface flags: No such device
Tue Jul 31 12:52:25 2018 - [info] OK.
Tue Jul 31 12:52:25 2018 - [warning] shutdown_script is not defined.
Tue Jul 31 12:52:25 2018 - [info] Got exit code 0 (Not master dead).
MySQL Replication Health is OK.
没有明显报错,只有几个警告而已,复制显示正常。
在hdp1上用root用户操作。
[root@hdp1~]#masterha_check_status --conf=/etc/masterha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
[root@hdp1~]#
显示"NOT_RUNNING",这代表MHA监控没有开启。执行下面的命令后台启动MHA。
mkdir -p /var/log/masterha/app1/
nohup masterha_manager --conf=/etc/masterha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/masterha/app1/manager.log 2>&1 &
启动参数说明:
再次检查MHA Manager的状态:
[root@hdp1~]#masterha_check_status --conf=/etc/masterha/app1.cnf
app1 (pid:298237) is running(0:PING_OK), master:172.16.1.127
[root@hdp1~]#
可以看见已经在监控了,而且master的主机为172.16.1.127。
在hdp1上用root用户操作。
[root@hdp1~]#tail -n20 /var/log/masterha/app1/manager.log
Tue Jul 31 12:57:06 2018 - [info]
172.16.1.127(172.16.1.127:3306) (current master)
+--172.16.1.126(172.16.1.126:3306)
+--172.16.1.125(172.16.1.125:3306)
Tue Jul 31 12:57:06 2018 - [info] Checking master_ip_failover_script status:
Tue Jul 31 12:57:06 2018 - [info] /usr/bin/master_ip_failover --command=status --ssh_user=root --orig_master_host=172.16.1.127 --orig_master_ip=172.16.1.127 --orig_master_port=3306
IN SCRIPT TEST====/sbin/ifconfig ens160:1 down==/sbin/ifconfig ens32:1 172.16.1.100===
Checking the Status of the script.. OK
SIOCSIFADDR: No such device
ens32:1: ERROR while getting interface flags: No such device
Tue Jul 31 12:57:06 2018 - [info] OK.
Tue Jul 31 12:57:06 2018 - [warning] shutdown_script is not defined.
Tue Jul 31 12:57:06 2018 - [info] Set master ping interval 1 seconds.
Tue Jul 31 12:57:06 2018 - [info] Set secondary check script: /usr/bin/masterha_secondary_check -s hdp4 -s hdp3 --user=root --master_host=hdp4 --master_ip=172.16.1.127 --master_port=3306
Tue Jul 31 12:57:06 2018 - [info] Starting ping health check on 172.16.1.127(172.16.1.127:3306)..
Tue Jul 31 12:57:06 2018 - [info] Ping(SELECT) succeeded, waiting until MySQL doesn't respond..
[root@hdp1~]#
在hdp4 172.16.1.127(master)上用root用户执行:
/sbin/ifconfig ens160:1 172.16.1.100/24
查看VIP:
[root@hdp4~]#ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
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: ens160: mtu 1500 qdisc mq state UP qlen 1000
link/ether 00:50:56:a5:49:7f brd ff:ff:ff:ff:ff:ff
inet 172.16.1.127/24 brd 172.16.1.255 scope global ens160
valid_lft forever preferred_lft forever
inet 172.16.1.100/16 brd 172.16.255.255 scope global ens160:1
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:497f/64 scope link
valid_lft forever preferred_lft forever
[root@hdp4~]#
(1)在slave1库(172.16.1.126)上停掉slave IO线程,模拟主从延时:
mysql -uroot -p123456 -e "stop slave io_thread;"
(2)在master库(172.16.1.127)安装sysbench,进行sysbench数据生成,在sbtest库下生成sbtest表,共10W记录。
# 用root用户安装sysbench
yum install sysbench -y
# 用mysql用户建立sbtest 数据库
mysql -uroot -p123456 -e "create database sbtest;"
# 用mysql用户执行sysbench生成数据
sysbench /usr/share/sysbench/tests/include/oltp_legacy/oltp.lua --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=root --mysql-password=123456 --oltp-test-mode=complex --oltp-tables-count=10 --oltp-table-size=10000 --threads=10 --time=120 --report-interval=10 --db-driver=mysql prepare
(3)用root用户停止master的mysql服务。
service mysql stop
(4)验证VIP漂移。
在hdp3上用root用户操作。
[root@hdp3~]#ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
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: ens32: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:50:56:a5:0f:77 brd ff:ff:ff:ff:ff:ff
inet 172.16.1.126/24 brd 172.16.1.255 scope global ens32
valid_lft forever preferred_lft forever
inet 172.16.1.100/16 brd 172.16.255.255 scope global ens32:1
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:f77/64 scope link
valid_lft forever preferred_lft forever
[root@hdp3~]#
在hdp4上用root用户操作。
[root@hdp4~]#ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
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: ens160: mtu 1500 qdisc mq state UP qlen 1000
link/ether 00:50:56:a5:49:7f brd ff:ff:ff:ff:ff:ff
inet 172.16.1.127/24 brd 172.16.1.255 scope global ens160
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:497f/64 scope link
valid_lft forever preferred_lft forever
[root@hdp4~]#
可以看到VIP已经从hdp4 172.16.1.127(master)漂移到了hdp3 172.16.1.126(slave1)。
(5)客户端用VIP访问数据库
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.100 -e "show databases; use sbtest; show tables; select count(*) from sbtest1; select count(*) from sbtest10;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sbtest |
| source |
| test |
+--------------------+
+------------------+
| Tables_in_sbtest |
+------------------+
| sbtest1 |
| sbtest10 |
| sbtest2 |
| sbtest3 |
| sbtest4 |
| sbtest5 |
| sbtest6 |
| sbtest7 |
| sbtest8 |
| sbtest9 |
+------------------+
+----------+
| count(*) |
+----------+
| 10000 |
+----------+
+----------+
| count(*) |
+----------+
| 10000 |
+----------+
C:\WINDOWS\system32>
在还没创建sbtest库的时候,172.16.1.126就停了slave sql线程。在新的Master 172.16.1.126上查看数据,可以看到落后的数据也同步过来了,数据没有丢失。
(6)查看复制的主从切换
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.125 -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: 172.16.1.126
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 19093607
Relay_Log_File: hdp2-relay-bin.000002
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 19093607
Relay_Log_Space: 455
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 126
Master_UUID: fadd5b7d-7d9f-11e8-90b4-13ccc7802b56
Master_Info_File: /data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.126 -e "show slave status\G"
mysql: [Warning] Using a password on the command line interface can be insecure.
C:\WINDOWS\system32>
可以看到,172.16.1.126称为新的master,而172.16.1.125也指向了这个新的master。
(7)检查MHA Manager的状态
在hdp1上用root用户执行下面的操作。
[root@hdp1~]#masterha_check_status --conf=/etc/masterha/app1.cnf
app1 is stopped(2:NOT_RUNNING).
[1]+ Done nohup masterha_manager --conf=/etc/masterha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/masterha/app1/manager.log 2>&1
[root@hdp1~]#
发现在执行了一次自动failover后,MHA Manager进程停止了。官网上对这种情况的解释如下:
意思是安装一个进程工具,通过该工具结合脚本来管理进程。
首先要还原环境。
还原数据库复制:
-- 在hdp4、hdp3、hdp2上重置master、slave
stop slave;
drop database sbtest;
reset master;
reset slave all;
-- 在hdp3、hdp2上重新指向hdp4为master
change master to
master_host='172.16.1.127',
master_port=3306,
master_user='repl',
master_password='123456',
master_log_file='mysql-bin.000001',
master_log_pos=120;
start slave;
show slave status\G
还原VIP绑定:
# 在hdp3上用root用户执行
/sbin/ifconfig ens32:1 down
# 在hdp4上用root用户执行
/sbin/ifconfig ens160:1 172.16.1.100
还原配置文件:
编辑在hdp1上/etc/masterha/app1.cnf,将[server1]段添加回去。
启动MHA Manage:
# 在hdp1上用root用户执行
nohup masterha_manager --conf=/etc/masterha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /var/log/masterha/app1/manager.log 2>&1 &
至此环境还原完毕,可以开始测试手工切换。当主服务器故障时,人工手动调用MHA来进行故障切换操作,步骤如下。
(1)停止MHA Manage
在hdp1上用root用户操作。
masterha_stop --conf=/etc/masterha/app1.cnf
(2)关闭master
在hdp4上用root用户操作。
service mysql stop
(3)执行手工切换
在hdp1上用root用户操作。
masterha_master_switch --master_state=dead --conf=/etc/masterha/app1.cnf --dead_master_host=172.16.1.127 --dead_master_port=3306 --new_master_host=172.16.1.126 --new_master_port=3306 --ignore_last_failover
(4)验证VIP漂移到172.16.1.126
[root@hdp3~]#ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
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: ens32: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:50:56:a5:0f:77 brd ff:ff:ff:ff:ff:ff
inet 172.16.1.126/24 brd 172.16.1.255 scope global ens32
valid_lft forever preferred_lft forever
inet 172.16.1.100/16 brd 172.16.255.255 scope global ens32:1
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:f77/64 scope link
valid_lft forever preferred_lft forever
[root@hdp3~]#
(5)验证复制关系
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.125 -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: 172.16.1.126
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 120
Relay_Log_File: hdp2-relay-bin.000002
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 120
Relay_Log_Space: 455
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 126
Master_UUID: fadd5b7d-7d9f-11e8-90b4-13ccc7802b56
Master_Info_File: /data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.126 -e "show slave status\G"
mysql: [Warning] Using a password on the command line interface can be insecure.
C:\WINDOWS\system32>
(6)验证客户端VIP访问
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.100 -e "show variables like 'server_id'; show databases;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| source |
| test |
+--------------------+
C:\WINDOWS\system32>
在许多情况下,需要将现有的主服务器迁移到另外一台服务器上。比如主服务器硬件故障,RAID控制卡需要重建,将主服务器移到性能更好的服务器上等等。维护主服务器引起性能下降,导致停机时间至少无法写入数据。另外,阻塞或杀掉当前运行的会话会导致主主之间数据不一致的问题发生。MHA 提供快速切换和优雅的阻塞写入,这个切换过程只需要 0.5-2s 的时间,这段时间内数据是无法写入的。在很多情况下,0.5-2s 的阻塞写入是可以接受的。因此切换主服务器不需要计划分配维护时间窗口。
MHA在线切换的大概过程:
注意,在线切换的时候应用架构需要考虑以下两个问题:
为了保证数据完全一致性,在最快的时间内完成切换,MHA的在线切换必须满足以下条件才会切换成功,否则会切换失败。
在测试前,先按照上面“测试手工切换”测试前的步骤执行还原环境(手工切换不用修改/etc/masterha/app1.cnf配置文件),然后按以下步骤测试线切换:
(1)停止MHA Manage
在hdp1上用root用户操作。
masterha_stop --conf=/etc/masterha/app1.cnf
(2)执行在线切换命令
在hdp1上用root用户操作。
masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=172.16.1.126 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000
(3)验证复制关系
在hdp2、hdp3、hdp4查看slave status:
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.125 -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: 172.16.1.126
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 120
Relay_Log_File: hdp2-relay-bin.000002
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 120
Relay_Log_Space: 455
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 126
Master_UUID: fadd5b7d-7d9f-11e8-90b4-13ccc7802b56
Master_Info_File: /data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.126 -e "show slave status\G"
mysql: [Warning] Using a password on the command line interface can be insecure.
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.127 -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: 172.16.1.126
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 120
Relay_Log_File: hdp4-relay-bin.000002
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 120
Relay_Log_Space: 455
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 126
Master_UUID: fadd5b7d-7d9f-11e8-90b4-13ccc7802b56
Master_Info_File: /data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
C:\WINDOWS\system32>
可以看到hdp3 172.16.1.126成为新的master,而hdp2 172.16.1.125和hdp4 172.16.1.127 成为指向新master的slave。
(4)验证VIP自动漂移
[root@hdp3~]#ip a
1: lo: mtu 65536 qdisc noqueue state UNKNOWN
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: ens32: mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:50:56:a5:0f:77 brd ff:ff:ff:ff:ff:ff
inet 172.16.1.126/24 brd 172.16.1.255 scope global ens32
valid_lft forever preferred_lft forever
inet 172.16.1.100/16 brd 172.16.255.255 scope global ens32:1
valid_lft forever preferred_lft forever
inet6 fe80::250:56ff:fea5:f77/64 scope link
valid_lft forever preferred_lft forever
[root@hdp3~]#
(5)验证客户端通过VIP访问数据库
C:\WINDOWS\system32>mysql -uroot -p123456 -h172.16.1.100 -e "show variables like 'server_id'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| server_id | 126 |
+---------------+-------+
C:\WINDOWS\system32>
通常情况下自动切换以后,原master可能已经废弃掉,待原master主机修复后,如果数据完整的情况下,可能想把原来master重新作为新主库的slave。这时我们可以借助当时自动切换时刻的MHA日志来完成对原master的修复。下面是提取相关日志的命令:
grep -i "All other slaves should start" /var/log/masterha/app1/manager.log
可以看到类似下面的信息:
All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='172.16.1.126', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000005', MASTER_LOG_POS=120, MASTER_USER='repl', MASTER_PASSWORD='123456';
意思是说,如果Master主机修复好了,可以在修复好后的Master执行CHANGE MASTER操作,作为新的slave库。