(5.15)mysql高可用系列——mysql mha实践

关键词:MHA,mysql mha

【1】需求

  采用mysql技术,实现MHA高可用主从环境,预计未来数据量几百G

  MHA概念参考:MYSQL高可用技术概述

【2】环境技术架构

【2.1】MHA简介

该软件由两部分组成:

  • MHA Manager(管理节点)
  • MHA Node(数据节点)

MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。

MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。

整个故障转移过程对应用程序完全透明。

可以将MHA工作原理总结为如下

  1. 从宕机崩溃的master保存二进制日志事件(binlog events)
  2. 识别含有最新更新的slave
  3. 应用差异的中继日志(relay log)到其他的slave;
  4. 应用从master保存的二进制日志事件(binlog events);
  5. 提升一个slave为新的master;
  6. 使其他的slave连接新的master进行复制;

【2.2】MHA工具包

Manager工具包

组件名称组件说明
masterha_check_ssh 检查MHA的SSH配置状况
masterha_check_repl 检查MySQL复制状况
masterha_manger 启动MHA
masterha_check_status 检测当前MHA运行状态
masterha_master_monitor 检测master是否宕机
masterha_master_switch 控制故障转移(自动或者手动)
masterha_conf_host 添加或删除配置的server信息

Node工具包

这些工具通常由MHA Manager的脚本触发,无需人为操作

组件名称组件说明
save_binary_logs 保存和复制master的二进制日志
apply_diff_relay_logs 识别差异的中继日志事件并将其差异的事件应用于其他的slave
filter_mysqlbinlog 去除不必要的ROLLBACK事件(MHA已不再使用这个工具)
purge_relay_logs 清除中继日志(不会阻塞SQL线程)

 

【2.3】基本操作环境与架构

  操作系统:5台 centos7.5

  数据库版本:mysql5.7.24

  MHA 软件  :MHA 0.58

  数据库架构:基于MHA 软件实现主从复制,采用GTID+无损同步复制技术,双主多从。

项目具体部署信息
角色 ip地址 主机名 server_id 类型
Monitor host 192.168.1.201 db1   监控复制组
master 192.168.1.202 db2 2023306 写入
slave1 192.168.1.203 db3 2033306 读(备用master)
slave2 192.168.1.204 db4 2043306
slave3 192.168.1.205 db5 2053306

 

 

 

 

 

 

 

 

【3】实践环境准备(搭建GTID+半同步的1主3从)

【3.0】注意事项

(1)不要将read_only=1写进从库的配置文件,因为主库宕机时,从库要提升为主库接受写请求  mysql -e"set global read_only=1"

(2)主从节点复制的过滤规则要相同,即binlog_do_db 与 binlog_ignore_db 参数主从配置需要相同

(3)从节点需要修改配置参数 relay_log_purge=0 ,即关闭中继日志的清除

(4)serverid不能一样

(5)安装目录的binlog最好一样

 

#采用命令方式将从库设为只读,不要将该参数写进配置文件中
mysql -e"set global read_only=1"
#关闭中继日志的清除
mysql -e"set global relay_log_purge=0"

【3.1】host

echo "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4">> /etc/hosts
echo "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6" >>/etc/hosts

echo "192.168.1.201 db1" >>/etc/hosts
echo "192.168.1.202 db2" >>/etc/hosts
echo "192.168.1.203 db3" >>/etc/hosts
echo "192.168.1.204 db4" >>/etc/hosts
echo "192.168.1.205 db5" >>/etc/hosts

【3.2】my.cnf

[client]
port = 3306
socket = /mysql/data/3306/mysql.sock
default-character-set=utf8

[mysql]
disable-auto-rehash #允许通过TAB键提示
default-character-set = utf8
connect-timeout = 10

[mysqld]
server-id = 3306
port = 3306
user = mysql
socket = /mysql/data/3306/mysql.sock
pid-file = /mysql/data/3306/mysql.pid
basedir = /mysql/app/mysql/
datadir = /mysql/data/3306/data
#bind_address = 10.10.10.11
autocommit = 0

character-set-server=utf8
explicit_defaults_for_timestamp=true
lower_case_table_names=1
back_log=103
max_connections=10000
max_connect_errors=100000
table_open_cache=512
external-locking=FALSE
max_allowed_packet=32M
sort_buffer_size=2M
join_buffer_size=2M
thread_cache_size=51
query_cache_size=32M
#query_cache_limit=4M
transaction_isolation=READ-COMMITTED
tmp_table_size=96M
max_heap_table_size=96M

###***logs
long_query_time = 10
slow_query_log = 1
slow_query_log_file=/mysql/log/3306/slow.log
log-error_verbosity=3
log-error = /mysql/log/3306/mysql.err
log_output = FILE #参数log_output指定了慢查询输出的格式,默认为FILE,你可以将它设为TABLE,然后就可以查询mysql架构下的slow_log表了

#log-queries-not-using-indexes
#log-slow-slave-statements
#general_log = 0
#general_log_file = /mysql/log/3306/mysql.log
#max_binlog_size = 1G
#max_relay_log_size = 1G



#replication_new
log_bin=/mysql/log/3306/mysql-bin #开启binlog
log_bin_index=/mysql/log/3306/mysql-bin.index
binlog_format=row
binlog_rows_query_log_events=on
max_binlog_size=2048

bind-address=0.0.0.0
server_id=2013306   #从库务必记得修改
expire_logs_days=7    #超过7天的binlog清理
innodb_support_xa=1
binlog_cache_size=1M
log_bin_trust_function_creators=1    #同步存储过程、函数、触发器
innodb_flush_log_at_trx_commit=1
sync_binlog=1
transaction-isolation=read-committed

#slave parameter 如果是从库,务必放开
#relay_log=/mysql/log/3306/relaylog/mysql-relay.log
#relay_log_purge=0
#read_only=1  
#slave-parallel-type=LOGICAL_CLOCK
#slave-parallel-workers=4
#master_info_repository=table #master_info 会记录到 mysql.slave_master_info
#relay_log_info_repository=table #relay_log 会记录到,mysql.slave_relay_log_info
#relay_log_recovery=1
#slave_skip_errors=ddl_exist_errors
#slave_preserve_commit_order=1

#5.7的增强半同步
#如果是5.7,参数前面加上loose_,如下列,如果是5.6 则直接使用 rpl_semi_sync_master_enabled=1 之类的就好了。
#我这里是5.7就直接做增强半同步了(loseless Semisynchronous )

plugin_dir=/mysql/app/mysql/lib/plugin/
plugin_load=rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so
loose_rpl_semi_sync_master_enabled=1 #MySQL开启主的半同步复制(rpl_semi_sync_master_enabled) 
loose_rpl_semi_sync_slave_enabled=1 #MySQL5.6开启从的半同步复制 
loose_rpl_semi_sync_master_timeout=5000 #超时5秒,切回异步 
rpl_semi_sync_master_wait_for_slave_count=1 #至少收到1个slave发会的ack 
rpl_semi_sync_master_wait_point=AFTER_SYNC #MySQL 5.7的方法,AFTER_SYNC(default value,增强半同步) & AFTER_COMMIT(传统半同步)

#GTID mode
gtid_mode=on
enforce_gtid_consistency=1
log-slave-updates=1
binlog_gtid_simple_recovery=1

 

【3.3】准备测试数据

  (主库上跑,即202那台机器)

-- 【3.3.1】创建复制用户
create user 'rpl'@'192.168.1.%' identified by '123456';
grant replication slave on *.* to 'rpl'@'192.168.1.%';
flush privileges;
select user,host from mysql.user;

-- 【3.3.2】构造测试数据
--  构造test库和test库下的test1,test2,test3表。test4表用于模拟业务一直在运行
create database test;
use test;
create table test1(id int);
insert into test1 values(1);
create table test2(id int);
insert into test2 values(2);
create table test3(id int);
insert into test3 values(3);
commit;
create table test4(id int);
insert into test4 values(4);
commit;

--  构造存储过程sp_test4来循环插入test4表,模拟业务运行
use test;
drop procedure if exists sp_test4;
delimiter $$
create procedure sp_test4()
begin
declare n int;
set n=11;
while(n<=20)
do
insert into test.test4 values(n);
commit;
set n=n+1;
end while;
end $$
delimiter ;

-- 构造事件,来调度sp_test4过程
use test;
set global event_scheduler=1;

delimiter $$
create event if not exists event_test4
on schedule every 5 second
on completion preserve
enable
do
begin
call sp_test4();
end $$
delimiter ;

-- 为了防止测试数据量累计导致卡顿,我这里5小时做一次truncate
delimiter $$
create event if not exists event_truncate_test4
on schedule every 5 hour
on completion preserve
enable
do
begin
truncate table test.test4;
end $$
delimiter ;

【3.4】基于xtrabackup的备份恢复初始化

-- 在202 主库上备份
innobackupex --defaults-file=/etc/my.cnf -uroot -p123456 --no-timestamp /mysql/backup/full.bak

-- 传输到从库
scp -r /mysql/backup/full.bak root@192.168.1.203:/mysql/backup/
scp -r /mysql/backup/full.bak root@192.168.1.204:/mysql/backup/
scp -r /mysql/backup/full.bak root@192.168.1.205:/mysql/backup/

#在从服务器还原
innobackupex --apply-log /mysql/backup/full.bak
service mysql stop
cd /mysql/data/3306
mkdir data
innobackupex --defaults-file=/etc/my.cnf --copy-back /mysql/backup/full.bak
chown -R mysql:mysql /mysql
chmod -R 755 /mysql

【3.5】主从配置

#在从服务器上执行
stop slave;
reset slave;
reset master;
SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;
set global gtid_purged='2c8b1813-e26f-11e9-adce-000c29658c19:1-417'; -- 这个从/mysql/backup/full.bak/xtrabackup_info 中获得
SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;
show master status;


change master to 
master_host='192.168.1.202',
master_user='rpl', 
master_password='123456',
master_port=3306, 
master_auto_position=1;

start slave;

【3.6】核验主从同步

(1)show slave status\G

(2)show processlist;

(3)select count(1) from test.test4;

 

【4】MHA

  安装参考:https://www.cnblogs.com/winstom/p/11022014.html

  概念:高可用架构方案  中的【3】MHA

【4.1】软件下载与依赖参考

【4.2】准备perl环境,安装 mha node 与 manager节点

需要在5台机器上都安装mha node,在monitor节点上安装 manager节点

#【4.2.1】安装依赖包: 监控节点manager(201)必须配置好网络yum源与epel源。其他的节点只需要本地yum源即可。 参考:yum源配置
#monitor节点 一定要先安装epel源
yum install epel-release 
yum install -y perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker perl-CPAN perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes
#【4.2.2】开始在所有节点安装node节点(这个可以只有操作系统的yum源)
cd mha4mysql-node-0.58
perl Makefile.PL
make && make install
#【4.2.2】/usr/local/bin/ 目录下会出现4个工具(node)
-r-xr-xr-x. 1 root root 17639 Oct 20 23:21 apply_diff_relay_logs
-r-xr-xr-x. 1 root root  4807 Oct 20 23:21 filter_mysqlbinlog
-r-xr-xr-x. 1 root root  8337 Oct 20 23:21 purge_relay_logs
-r-xr-xr-x. 1 root root  7525 Oct 20 23:21 save_binary_logs
Node脚本说明:(这些工具通常由MHA Manager的脚本触发,无需人为操作)

save_binary_logs               //保存和复制master的二进制日志
apply_diff_relay_logs          //识别差异的中继日志事件并将其差异的事件应用于其他的slave
filter_mysqlbinlog             //去除不必要的ROLLBACK事件(MHA已不再使用这个工具)
purge_relay_logs               //清除中继日志(不会阻塞SQL线程)

 #【4.2.3】开始在201安装manager(【注意查看【4.2】的yum依赖包安装,否则该步骤会部分操作失败】)

cd mha4mysql-manager-0.58
perl Makefile.PL
make && make install

#【4.2.4】把上面生成的工具命令所在目录添加到环境变量

 echo "export PATH=${PATH}:/usr/local/bin">>/etc/profile source /etc/profile

 #201机器,安装完node和manager节点之后,/usr/local/bin 目录如下

  

【4.3】秘钥互信及复制mysql共享库到系统库

  (要MHA故障自动转移必须要这个)

(1)秘钥互信
配置所有机器相互之间root用户秘钥互信 在所有机器上执行: 生成密钥对   
ssh-keygen -t dsa -f ~/.ssh/id_rsa -P "" #推送公钥 ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.1.201 ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.1.202 ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.1.203 ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.1.204 ssh-copy-id -i /root/.ssh/id_rsa.pub root@192.168.1.205 #此时所有的机器之间以完成互信,无需密码等即可ssh登陆

(2)复制mysql客户端共享库到Linux系统库下
cp /mysql/app/mysql/lib/libmysqlclient.so.20 /lib64/

【4.4】MHA配置文件及relay_log清理脚本(4.4.3)

在monitor,即201机器上配置

#【4.4.1】复制监控节点配置文件
mkdir
-p /etc/masterha
cp /soft/mha4mysql-manager-0.58/samples/conf/app1.cnf  /etc/masterha/

#默认引用配置文件位置为:/etc/masterha_default.cnf

#【4.4.2】修改监控节点配置文件
mkdir -p /var/log/masterha/app1
touch /var/log/masterha/app1/manager.log
chmod -R 777 /var/log/masterha/
vim /etc/masterha/app1.cnf

#如果每个机器的binlog位置不一样,建议参考一下 故障处理中的(10)
[server default]
manager_workdir=/var/log/masterha/app1 #管理节点工作目录
manager_log=/var/log/masterha/app1/manager.log #管理节点日志
master_binlog_dir=/mysql/log/3306 #数据库Binlog所在日志
master_ip_failover_script=/usr/local/bin/master_ip_failover #自动故障转移的脚本
master_ip_online_change_script=/usr/local/bin/master_ip_online_change #手动在线切换主节点的脚本
password=123456   #mysql数据库监控密码
user=root #mysql数据库监控用户
ping_interval=1   #发送ping间隔包的时间,默认3S,这里1是1S,如果3次没有响应则故障切换
remote_workdir=/tmp #mysql切换的时候binlog保存路径
repl_password=123456 #mysql复制的mysql用户密码
repl_user=rpl #mysql复制的Mysql用户账户
report_script=/usr/local/bin/send_report #发生切换之后的报警报告脚本
secondary_check_script=/usr/local/bin/masterha_secondary_check  -s db2 -s db3 -s db4 -s db5 #检测哪个库是最新的,以便故障转移时为新主库
shutdown_script=""  #关闭的脚本
ssh_user=root #SSH通信的用户

[server1]
hostname=192.168.1.202
port=3306

[server2]
hostname=192.168.1.203
port=3306
candidate_master=1  #设置为备用主库,当多个SERVER都有设置它,用最新的做主库
check_repl_delay=0 #切换的时候忽略复制延迟,

#默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间;
#通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master

[server3]
port=3306
hostname=192.168.1.204

[server4]
hostname=192.168.1.205
port=3306
no_master=1 #不会成为主库

直接可用配置参数代码:
[server default]
manager_workdir=/var/log/masterha/app1
manager_log=/var/log/masterha/app1/manager.log

master_binlog_dir=/mysql/log/3306
master_ip_failover_script=/usr/local/bin/master_ip_failover
master_ip_online_change_script=/usr/local/bin/master_ip_online_change
password=123456
user=root
ping_interval=1
remote_workdir=/tmp
repl_password=123456
repl_user=rpl
report_script=/usr/local/bin/send_report
secondary_check_script=/usr/local/bin/masterha_secondary_check  -s db2 -s db3 -s db4 -s db5
shutdown_script=""
ssh_user=root

[server1]
hostname=192.168.1.202
port=3306
candidate_master=1

[server2]
hostname=192.168.1.203
port=3306
candidate_master=1
check_repl_delay=0

[server3]
port=3306
hostname=192.168.1.204

[server4]
hostname=192.168.1.205
port=3306
no_master=1

 



#配置文件说明
MHA主要配置文件说明

manager_workdir=/var/log/masterha/app1.log:设置manager的工作目录
manager_log=/var/log/masterha/app1/manager.log:设置manager的日志文件
master_binlog_dir=/data/mysql:设置master 保存binlog的位置,以便MHA可以找到master的日志
master_ip_failover_script= /usr/local/bin/master_ip_failover:设置自动failover时候的切换脚本
master_ip_online_change_script= /usr/local/bin/master_ip_online_change:设置手动切换时候的切换脚本
user=root:设置监控mysql的用户
password=dayi123:设置监控mysql的用户,需要授权能够在manager节点远程登录
ping_interval=1:设置监控主库,发送ping包的时间间隔,默认是3秒,尝试三次没有回应的时候自动进行railover
remote_workdir=/tmp:设置远端mysql在发生切换时binlog的保存位置
repl_user=repl :设置mysql中用于复制的用户密码
repl_password=replication:设置mysql中用于复制的用户
report_script=/usr/local/send_report:设置发生切换后发送的报警的脚本
shutdown_script="":设置故障发生后关闭故障主机脚本(该脚本的主要作用是关闭主机放在发生脑裂,这里没有使用)
ssh_user=root //设置ssh的登录用户名
candidate_master=1:在节点下设置,设置当前节点为候选的master
slave check_repl_delay=0 :在节点配置下设置,默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master;这个选项对于对于设置了candidate_master=1的主机非常有用
secondary_check_script=/usr/local/bin/masterha_secondary_check -s db2 -s db3 -s db4 -s db5 #检测哪个库是最新的,以便故障转移的时候确认信主库
 

 

【4.4.3】设置定期清理relay脚本(node节点服务器,即202~205)

#在202机器上执行
弄之前,先给主库,再加个IP网卡 :/sbin/ifconfig ens34:1 192.168.1.210/24,然后作为VIP使用
mysql -uroot -p123456 -e"set global relay_log_purge=0;" #所有202~205都执行
vim /usr/local/bin/purge_relay_log.sh

#新建/修改如下
#!/bin/bash
user=root  #mysql的账户密码
password=123456
port=3306
socket=/mysql/data/3306/mysql.sock
log_dir='/var/log/masterha/log'
work_dir='/mysql/log/3306/relaylog'
purge='/usr/local/bin/purge_relay_logs'
if [ ! -d $log_dir ]
then
   mkdir -p $log_dir
fi
$purge --user=${user} --password=${password} -S ${socket} --host=localhost --disable_relay_log_purge --port=${port}

#复制到202~205上的每一台机器上去

  scp root@192.168.1.202:/usr/local/bin/purge_relay_log.sh /usr/local/bin

#添加到crontab,每4小时执行一次

  chmod +x /usr/local/bin/purge_relay_log.sh

  crontab -e

  0 4 * * * /bin/bash /usr/local/bin/purge_relay_log.sh

  #4个小时清理一次

 

#手工执行以下查看该脚本是否报错

 

 

【4.5】VIP配置

  这些官方有脚本,但是这是比较新的脚本,我们还是用我们自己的老脚本

    

  (1)手工用脚本实现  

    【4.5.1】自动故障转移VIP脚本(master_ip_failover)    
    【4.5.2】手动故障转移VIP配置脚本(master_ip_online_change)

  (2)keepalived

手动配置VIP

注意,考虑到所有主机全部重启情况;

#判断主库 select * from information_schema.processlist where state like 'Master%';
#判断从库 select * from information_schema.processlist where state like 'Slave%';
#在主库上,手动添加vip地址
nohup ping -c 2 192.168.1.210 if [ $? != 0 ];then /sbin/ifconfig ens34:1 192.168.1.210/24 fi #在主、备主上,根据情况脚本设置开机自启 cat << eof >>/etc/rc.d/rc.local nohup ping -c 2 192.168.1.210 if [ $? != 0 ];then /sbin/ifconfig ens34:1 192.168.1.210/24 fi eof

 如何删掉这个手动添加的VIP地址?

ifconfig ens34:1 down
#ip addr del 192.168.1.210 dev ens34

#开启
ifconfig ens34:1 up
ifup ifcfg-ens34:1

 也可以通过构建一个新的网口网卡http://www.lwops.cn/forum.php?mod=viewthread&tid=311&fromuid=1&tdsourcetag=s_pctim_aiomsg

大致步骤:

  (1)在主、备主上,复制 cp /etc/sysconfig/network-script/ifcfg-ens34   /etc/sysconfig/network-script/ifcfg-ens34:1 ,配置好IP地址为VIP

  (2)先在主库上启动这个网卡 ifup ifcfg-ens34:1  ,(注意不同主和备注 两个同事开启,会IP地址冲突的)

  (3)在两台机器上都添加下面脚本,并添加到开机启动,以防有机器宕机重启后 脚本不执行了。  避免配置问题不要使用 systemctl restart network 以防有问题直接远程都连不上,单独开启这个网卡用这个 ifup ifcfg-ens34:1

#在主、备主上,根据情况脚本设置开机自启
cat << eof >>/etc/rc.d/rc.local
nohup ping -c 2 192.168.1.210
if [ $? != 0 ];then
/sbin/ifconfig ens34:1 192.168.1.210/24
fi
eof

 

 手动故障转移等脚本配置


【4.5.1】MHA自动故障转移VIP脚本(master_ip_failover)
弄之前,先给主库,再加个IP网卡 :/sbin/ifconfig ens34:1 192.168.1.210/24,然后作为VIP使用
#修改 master_ip_failover脚本,使用脚本管理VIP
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.210/24';
my $key = '1';
my $ssh_start_vip = "/sbin/ifconfig ens34:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens34:$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" ) {

        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";
        exit 0;
    }
    else {
        &usage();
        exit 1;
    }
}

sub start_vip() {
    `ssh $ssh_user\@$new_master_host \" $ssh_start_vip \"`;
}
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";
}

 

【4.5.2】手动故障转移VIP配置脚本(master_ip_online_change)
弄之前,先给主库,再加个IP网卡 :/sbin/ifconfig ens34:1 192.168.1.210/24,然后作为VIP使用
vim /usr/local/bin/master_ip_online_change
(1)官方版
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use Getopt::Long;
use MHA::DBHelper;
use MHA::NodeUtil;
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 = '192.168.1.210/24';  # Virtual IP 
my $key = "1"; 
my $ssh_start_vip = "/sbin/ifconfig ens34:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens34:$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" );
      next if ( 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 nobody can connect. Disabling per-session binlog beforehand
      #$orig_master_handler->disable_log_bin_local();
      #print current_time_us() . " Drpping app user on the orig master..\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 are disconnected.. (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(except SUPER) 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 can complete
      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 are disconnected.. (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 VIP on old master: $orig_master_host \n";
                &stop_vip();     


      ## Terminating all threads
      print current_time_us() . " Killing all application threads..\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 though activating updatable accounts/ip failed so that we don't interrupt slaves' recovery.
# If exit code is 0 or 10, MHA does not abort
    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 new master.\n";
      $new_master_handler->disable_read_only();

      ## Creating an app user on the new master
      #print current_time_us() . " Creating app user on the new master..\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 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_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;
}

(2)简单版
#!/bin/bash
source /root/.bash_profile

vip=`echo '192.168.1.210/24'`  #设置VIP
key=`echo '1'`

command=`echo "$1" | awk -F = '{print $2}'`
orig_master_host=`echo "$2" | awk -F = '{print $2}'`
new_master_host=`echo "$7" | awk -F = '{print $2}'`
orig_master_ssh_user=`echo "${12}" | awk -F = '{print $2}'`
new_master_ssh_user=`echo "${13}" | awk -F = '{print $2}'`

#要求服务的网卡识别名一样
stop_vip=`echo "ssh root@$orig_master_host /usr/sbin/ifconfig bond0:$key down"`
start_vip=`echo "ssh root@$new_master_host /usr/sbin/ifconfig bond0:$key $vip"`

if [ $command = 'stop' ]
  then
    echo -e "\n\n\n****************************\n"
    echo -e "Disabled thi VIP - $vip on old master: $orig_master_host \n"
    $stop_vip
    if [ $? -eq 0 ]
      then
    echo "Disabled the VIP successfully"
      else
    echo "Disabled the VIP failed"
    fi
    echo -e "***************************\n\n\n"
  fi

if [ $command = 'start' -o $command = 'status' ]
  then
    echo -e "\n\n\n*************************\n"
    echo -e "Enabling the VIP - $vip on new master: $new_master_host \n"
    $start_vip
    if [ $? -eq 0 ]
      then
    echo "Enabled the VIP successfully"
      else
    echo "Enabled the VIP failed"
    fi
    echo -e "***************************\n\n\n"
fi
【4.5.3】授权
chmod +x /usr/local/bin/master_ip_failover
chmod +x /usr/local/bin/master_ip_online_change

【4.5.4】keepalived(选用)

本文基于手动生成VIP,没有使用keepalived

详细keepalived配置请参考其他文章,配置好keepalived后,mha相关文件配置如下

master_ip_failover
my $vip = '192.168.8.88/24';
my $key = '1';
my $ssh_start_vip = "systemctl start keepalived";
my $ssh_stop_vip = "systemctl stop keepalived";
master_ip_online_change

my $vip = '192.168.8.88/24';
my $key = '1';
my $ssh_start_vip = "systemctl start keepalived";
my $ssh_stop_vip = "systemctl stop keepalived";
my $orig_master_ssh_port = 22;
my $new_master_ssh_port = 22;
my $ssh_user = "root";
my $new_master_password='mysql';
my $orig_master_password='mysql';


三、添加非本机 IP 邦定支持
echo "net.ipv4.ip_nonlocal_bind=1" >> /etc/sysctl.conf sysctl -p

 

【4.6】配置检查(在201上,即manager节点上)

【4.6.1】检查SSH配置
#检查mha manager 到所有的 MHA Node 的SSH链接状态
masterha_check_ssh --conf=/etc/masterha/app1.cnf
【4.6.2】检查整个复制环境状态
#通过 masterha_check_repl 脚本查看整个集群的状态
masterha_check_repl --conf=/etc/masterha/app1.cnf
【4.6.3】检查 MHA Manager 的状态
masterha_check_status --conf=/etc/masterha/app1.cnf
【4.6.4】手动启动MHA监控
mkdir -p /var/log/masterha/app1
chmod -R 777 /var/log/masterha/app1

#启动MHA监控 nohup masterha_manager --conf=/etc/masterha/app1.cnf --ignore_laster_failover &
#--remove_dead_master_conf &

(1)--remove_dead_master_conf #当发生故障切换后,老的主库配置会从配置文件中移除掉(比如发生故障切换了,202主库切换到了203,那么在app1.cnf配置文件中,[server1]描述的ip为192.168.1.202内容会被清除)
(2)--manger_log #管理节点的日志位置
(3)--ignore_last_failover
  #在默认情况下,如果mha检查到主库连续发现宕机,且两次宕机间隔不超过8小时,则不会发生继续切换。(会生成一个文件,判断文件存在就不允许继续故障转移切换)
  #加了这个参数的话,就是来避免上述情况的,以便可以无限制的故障切换。


# 停止MHA监控
masterha_stop --conf=/etc/masterha/app1.cnf
 

  

【4.7】自动故障转移切换测试

切换之后,注意修改my.cnf,比如read_only等等

【4.7.1】直接关闭主库202机器的mysql

systemctl stop mysql #service mysql stop

【4.7.2】查看日志了解故障转移切换原理

#查看日志 /var/log/masterha/app1/manager.log
#这里只筛选了一下步骤

** Phase 1: Configuration Check Phase completed.
* Phase 2: Dead Master Shutdown Phase..
* Phase 2: Dead Master Shutdown Phase completed.
* Phase 3: Master Recovery Phase..
* Phase 3.1: Getting Latest Slaves Phase..
* Phase 3.3: Determining New Master Phase..
* Phase 3.3: New Master Recovery Phase..
* Phase 3: Master Recovery Phase completed.
* Phase 4: Slaves Recovery Phase..
* Phase 4.1: Starting Slaves in parallel..
* Phase 5: New master cleanup phase..

由此可以看出,MHA故障转移详细步骤;
(1)配置文件检查阶段:通过检查MHA的配置文件,获取相关的MHA集群机器信息,故障转移脚本信息等等
(2)宕机主库关闭阶段:将VIP删除
(3)复制宕机master库的binlog与最新slave库的差异relay log,保存到monitor节点下。
(4)推选识别含有最新数据的slave库,提升为master库。
(5)新master库应用(3)中保存下来的二进制日志
(6)将其他的slave库连接到新的master库,进行复制

(1)从宕机崩溃的master保存二进制日志事件(binlog events);

(2)识别含有最新更新的slave;

(3)应用差异的中继日志(relay log)到其他的slave;

(4)应用从master保存的二进制日志事件(binlog events);

(5)提升一个slave为新的master;

(6)使其他的slave连接新的master进行复制;

1.检查阶段

<1>.监测到maser goway
<2>.多路由检测
<3>.重复检测4次(这个次数固定的,无法修改)
<4>.检测ssh是否正常
<5>.检测配置(主要检测集群中有哪些机器,哪些机器可以正常访问,哪些机器已经dead,binlog,
候选master,relay_log_purge,read_only)
<6>.再一次检测master状态

 
2.关闭服务阶段
为了防止造成数据异常,关闭服务器阶段。可以调用 master_ip_failover_scripts 脚本关闭服务器

3.恢复阶段
(1).master恢复阶段
  <1>.找出最近的slave和最旧的slave.
  <2>.复制未发送出去的binlog并保存在manager上(T1).
  <3>.使用最新的slave恢复其他的slave,使所有的slave处于同一个时间点
  <4>.选取新的master
  <5>.将T1和master进行对比,生成差异binlog,并重做,保存此时的binlog和pos(T2)   
此时可以调用master_ip_failover_scripts对外开启服务
(2).slave恢复阶段
并行将T1和slave对比生成差异binlog,并重做

(3).slave信息修改阶段
  (1).在newmaster上执行reset slave all  
  (2).在其他slave上执行change master binlog和pos在T2
  (3).修改mha配置文件


4.检查阶段(报告阶段)
可以调用report_scripts脚本检查新的集群是否正常

 

【4.7.3】核验数据

(1)打开 schedure_event,查看test.test4

(2)查看show slave status

(3)建立一个库、表、数据,查看是否同步

(4)如何查看vip是否切换成功?

  mysql -uroot -p123456 -h192.168.1.210 #通过VIP登录

  show variables like 'hostname';

    

 

   最终发现,VIP还是切换过来了。

 

【4.7.4】MHA 官方的 BUG,故障转移后 manager进程死掉

对于 201 机器 monitor ,发生故障转移后,manager进程直接死掉了,即MHA监控脚本已经自动停止。

这个时候如果再次发生故障,就无法自动故障转移切换了。详情如下图:

  

解决办法:

  官方的回复是,不想让这个进程死掉,就放到后台运行,但我们是后台启动的,并没有效果;

   我们可以写一个脚本,监控进程是否存在,如果进程不存在则启动它。感觉就如同mysqld_safe一样,守护着mysql,mysql挂了就自动拉起来。

vim /usr/local/bin/manager_status_check

#!/bin/bash
while true
do
mha_check=`ps -ef|grep masterha_manager|grep -v grep|wc -l`
if [ ${mha_check} -eq 0 ];then
nohup masterha_manager --conf=/etc/masterha/app1.cnf --ignore_laster_failover &
#--remove_dead_master_conf &
else 
   echo "MHA manager start"
fi
sleep 5
done

#授权及加入开机自启
#chmod u+x /usr/local/bin/manager_status_check
#echo "nohup
/usr/local/bin/manager_status_check">>/etc/rc.d/rc.local

 【4.7.5】原本宕机的主节点重新加入回

 (1)如果掉线时间长,宕机时间段内数据量大,建议操作如下(或者直接进行备份还原操作

增强半同步从库宕机如何重新连入主库?

1. 此2个参数rpl_semi_sync_master_enabled  和rpl_semi_sync_slave_enabled  不要直接写入到my.cnf配置文件开启。
2.在slave库上先 stop slave io_thread ;set global  rpl_semi_sync_slave_enabled=0 关闭此参数。
  然后start slave io_thread 或者start slave 开启异步复制,让slave库追赶上master库。
3.然后在slave库 set global  rpl_semi_sync_slave_enabled=1 ;stop  slave io_thread;start  slave  io_thread;

(2)否则直接运行下列脚本重新制定主从即可

现在主库是203啦。

change master to 
master_host='192.168.1.203',
master_user='rpl', 
master_password='123456',
master_port=3306, 
master_auto_position=1;

注意,如果monitor 启动的时候加了

--remove_dead_master_conf

 那么配置文件会把之前宕机的机器信息(也就是202)清除掉,所以我们需要修改一下配置文件,把202加回来

左边修改前,右边修改后。

 

【4.8】手工切换主库(宕机切换与在线切换,masterha_master_switch 与 master_ip_online_change)

切换之后,注意修改my.cnf

做这块操作之前,必须要先关掉 masterha_manger.

ps -ef|grep manager*

【4.8.1】 masterha_master_switch
#在线切换(当前主库是203,想切换到202为主库)masterha_master_switch --conf=/etc/masterha/app1.cnf --master_state=alive --new_master_host=192.168.1.202 --new_master_port=3306 --orig_master_is_new_slave --running_updates_limit=10000

 会提示要你先在主库进行 FLUSH NO_WRITE_TO_BINLOG TABLES
 作用是: 关闭所有打开的表,强制关闭所有正在使用的表,并刷新查询缓存和预准备语句缓存。还会从查询缓存中删除查询结果。
 默认情况下flush语句会写入binlog,这里使用no_write_to_binlog禁止记录。查看Binlog发现,binlog内真的啥都没记录。

#其实会调用到 master_ip_online_change 脚本
#master_ip_online_change 具体文件配置参考【4.5】VIP配置

【4.8.1】核验
参考:【4.7.3】核验数据

记得把监控启动起来

【4.8.2】MHA在线切换的考虑与基本原理

在许多情况下, 需要将现有的主服务器迁移到另外一台服务器上。 比如主服务器硬件故障,RAID 控制卡需要重建,将主服务器移到性能更好的服务器上等等。维护主服务器引起性能下降, 导致停机时间至少无法写入数据。 另外, 阻塞或杀掉当前运行的会话会导致主主之间数据不一致的问题发生。 MHA 提供快速切换和优雅的阻塞写入,这个切换过程只需要 0.5-2s 的时间,这段时间内数据是无法写入的。在很多情况下,0.5-2s 的阻塞写入是可以接受的。因此切换主服务器不需要计划分配维护时间窗口。

MHA在线切换的大概过程:

  1. 检测复制设置和确定当前主服务器
  2. 确定新的主服务器
  3. 阻塞写入到当前主服务器
  4. 等待所有从服务器赶上复制
  5. 授予写入到新的主服务器
  6. 重新设置从服务器

注意,在线切换的时候应用架构需要考虑以下两个问题:

  1. 自动识别master和slave的问题(master的机器可能会切换),如果采用了vip的方式,基本可以解决这个问题。
  2. 负载均衡的问题(可以定义大概的读写比例,每台机器可承担的负载比例,当有机器离开集群时,需要考虑这个问题)

为了保证数据完全一致性,在最快的时间内完成切换,MHA的在线切换必须满足以下条件才会切换成功,否则会切换失败。

  1. 所有slave的IO线程都在运行
  2. 所有slave的SQL线程都在运行
  3. 所有的show slave status的输出中Seconds_Behind_Master参数小于或者等于running_updates_limit秒,如果在切换过程中不指定running_updates_limit,那么默认情况下running_updates_limit为1秒。
  4. 在master端,通过show processlist输出,没有一个更新花费的时间大于running_updates_limit秒。

 

【5】增删节点

【5.1】增加节点

(1)新节点机器 安装agent节点环境,初始化mysql数据库

(2)新节点机器 异步复制跟上

(3)新节点机器 半同步复制跟上

(4)与manager 秘钥互信

(5)修改manager配置文件

(6)重启manager服务

(7)复制检查

 

【5.2】删除节点

(1)关闭删除节点复制
(2)停掉删除节点服务/清除slave 连接 master信息(reset slave)
(3)修改配置文件
(4)重启manager

(5)复制检查

【6】MHA binlog server(可不配)

【6.1】binlogserver是什么?

它其实就是用来存放主库的binlog日志的,避免 主库挂掉,丢失更多数据;

因为是直接获取binlog,而不需要再写、写应用,所以效率快,只要网络可以,就没问题

1、准备一台新的mysql实例(db03)--也可以是manager节点,GTID必须开启。

2、将来binlog接收目录,不能和主库binlog目录一样

3、秘钥互信是必须的

【6.2】开启binlogserver

vim /etc/mha/app1.cnf     //添加如下模块
 
[binlog1]
no_master=1
hostname=10.0.0.53                              ----> 主机DB03
master_binlog_dir=/data/mysql/binlog/    ----> binlog保存目录  

【6.3】使用mysqlbinlog 命令实现 binlog的同步

mkdir -p /data/mysql/binlog/
chown -R mysql.mysql /data/mysql
 
# 进入目录启动程序
  cd /data/mysql/binlog/ &&\
  mysqlbinlog  -R --host=10.0.0.51 --user=mha --password=mha --raw  --stop-never mysql-bin.000001 &
   
参数说明:-R 远程主机,注意,这里的IP地址,应该是vip还是当前主IP呢?应该是物理地址,所以每一次故障转移,就要手动重新启动一次mysqlbinlog命令,重新设置IP地址和binlog位置参数,太蛋疼了

【6.4】启动

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 &

【6.5】测试验证

#查看binlog目录中的binlog
[root@db03 binlog]# ll
total 44
-rw-r--r-- 1 root root 285 Mar  8 03:11 mysql-bin.000001
 
#登录主库
[root@mysql-db01 ~]# mysql -uroot -p123
 
#刷新binlog
mysql> flush logs;
 
#再次查看binlog目录
[root@db03 binlog]# ll
total 48
-rw-r--r-- 1 root root 285 Mar  8 03:11 mysql-bin.000001
-rw-r--r-- 1 root root 143 Mar  8 04:00 mysql-bin.000002

 

 

 附录:故障解决

(1)perl-Log-Dispatch no available packages

     perl-Parallel-ForkManager no available packages

 

  解决办法:

    yum -y install epel-release ,安装了之后, 再重新 

      yum install -y perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker perl-CPAN perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes

(2)手动VIP如何在重启后正确的重新设置? 

cat << eof >>/etc/rc.d/rc.local
nohup ping -c 2 192.168.1.210
if [ $? != 0 ];then
/sbin/ifconfig ens34:1 192.168.1.210/24
fi
eof

#判断主库 select * from information_schema.processlist where state like 'Master%';
#判断从库 select * from information_schema.processlist where state like 'Slave%';

 (3)官方BUG,故障转移之后,monitor服务器下的manager程序死掉了

 详情见本文【4.7.4】

(4) masterha_check_repl(1130,1045) :Access denied for user 'root'@'mha1'

故障原因:

此账号没有权限登录到对应的机器上

处理方法:

为对应的用户授权即可

(5)masterha_check_repl(Can't exec "mysqlbinlog"):从当前环境变量中找不到binlog

故障原因:

从当前的环境变量中找不到 mysqlbinlog 命令

解决方法:

将 mysqlbinlog 的路径添加到 环境变量中

(6) masterha_check_repl(rep no exist or does not have REPLICATIONSLAVE privilege)

故障原因

缺少 REPLICATION SLAVE 权限

解决方法:

为同步账号添加 REPLICATION SLAVE 权限即可, 注意,是所有节点都添加, 保证主从切换后都可以正常使用。

(7)event_scheduler导致常连接问题

  

故障原因:

这个是由于部属的 mha 版本没有跟上 数据库的版本. 在检测长连接时, 由于系统新增加了event_scheduler 功能,且属于打开的状态,那么此用户会一直存在, mha 检测时将其列为长连接,所以出现上面错误

解决方法:

临时解决方法: 禁用 event_scheduler, set global event_scheduler = 0;

长久之计,按下面方式修改源码:

(8) mha 管理 vip, 节点之间的网卡名不一样,切换会失败

解决方法:

  * 改网卡名

  * 改切换脚本

2.6、 mha 管理 vip, ssh 默认端口非22

  切换会失败

解决方法:

  * 改默认端口

  * 改切换脚本

注: 在线切换 和 故障切换脚本QQ群中提供 群号:748415432

(9) 使用 GTID 时切换的坑(gtid_mode=1; auto_position=0)

  gtid_mode=1; auto_position=0 模式, 配置 binlog server 选项

虽然打开了 GTID, 但同步依旧使用的是log_file + position 模式同步数据, 切换时依旧自动转成 auto_position=1 模式, 转换后很有可能出来 1236 同步错误. 下面两段代码解释了为什么会依旧使用 auto_position=1 模式 .

  
  

2.8、 gtid_mode=1; auto_position=0模式,

  配置 binlog server 选项, 同时配置了 use_gtid_auto_pos=0

  看似解决了上面的问题, 但引入了一个最大的问题, 不补尝原主实例的差异数据了, 这就是说, 原主库任何情况下出现异常都属于机器挂的情况‘

2.9、 gtid_mode=1; auto_position=1模式,

  没有配置 binlog server 选项, 依旧补不了日志。

2.7 ~ 2.9 解决方案:

  开启 gitd 后, 最好的方案就是 基于 gtid 同步, 且使用 auto_position=1, 同时配置 binlog server 选项。

(10)如果新实例,则需要执行一个事务,才可以被识别为开启 了 GTID 模式

[server default]

# 这边是 连接 MySQL 的账号与密码, 如果端口发生改变, 也要写上相应的端口, 默认为 330
port=3306
user=rootpassword=123456# 这边是连接机器的 ssh 用户,密码使用互信方式实现
ssh_user=root# 这边复制账号
repl_user=repl
repl_password=123456
master_binlog_dir= /data/mysql/mysqldata3306/binlog master_ip_failover_script= /etc/mha/scripts/ master_ip_failover_new master_ip_online_change_script= /etc/mha/scripts/master_online_change_new manager_workdir=/etc/mha/app1manager_log=/etc/mha/log/mha/manager.log
[server1]
hostname=192.168.1.20 candidate_master=1 master_binlog_dir= /data/mysql/mysqldata3306/binlog
[server2]
hostname=192.168.1.2 1candidate_master=1 master_binlog_dir= /data/mysql/mysqldata3306/binlog
[server3]
hostname=192.168.1.22 candidate_master=1 master_binlog_dir= /data/mysql/mysqldata3306/binlog
[server4]
hostname=192.168.1.23 candidate_master=1 master_binlog_dir= /data/mysql/mysqldata3306/binlog
[binlog1]
hostname=192.168.1.20 [binlog2] hostname=192.168.1.21 [binlog3] hostname=192.168.1.22 [binlog4] hostname=192.168.1.23

 

结尾

想要完美的避开上面的坑, 建议:

* 使用高版本的 MHA, 可以解决上面切换的坑.

* 如果打开了 GTID 模式,则使用 auto_position=1 同步模式,同时 MHA 的配置文件中 配置[binlog1] 选项, 地址写上原主库地址就好, 不需要真实配置一个 binlog server 服务器

本文分享自微信公众号 - 3306pai(pai3306)

 

以下部分转自:https://www.cnblogs.com/xuliuzai/p/11980273.html

(11):masterha_check_repl 副本集方面报错  replicates is not defined in the configuration file!

具体信息如下:

复制代码
# /usr/local/bin/masterha_check_repl --conf=/etc/mha/app1.cnf
Thu Nov 21 15:33:15 2018 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping. Thu Nov 21 15:33:15 2018 - [info] Reading application default configuration from /etc/mha/app1.cnf.. Thu Nov 21 15:33:15 2018 - [info] Reading server configuration from /etc/mha/app1.cnf.. Thu Nov 21 15:33:15 2018 - [info] MHA::MasterMonitor version 0.56. Thu Nov 21 15:33:16 2018- [error][/usr/local/share/perl5/MHA/ServerManager.pm, ln671] Master 179.179.19.179:3306 from which slave 179.179.19.180(179.179.19.180:3306) replicates is not defined in the configuration file! Thu Nov 21 15:33:16 2018 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln424] Error happened on checking configurations. at /usr/local/share/perl5/MHA/MasterMonitor.pm line 326. Thu Nov 21 15:33:16 2018 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln523] Error happened on monitoring servers. Thu Nov 21 15:33:16 2018 - [info] Got exit code 1 (Not master dead). MySQL Replication Health is NOT OK!
复制代码

分析:MHA 漂移过后,我们知道配置信息中 主节点的信息就不在了,我们需要及时维护,否则/usr/local/bin/masterha_check_repl --conf=/etc/mha/XXX.cnf 检查副本集状态报错。

(12) masterha_master_switch 在线切换方面 报错 We should not start online master switch when one of connections are running long updates on the current master

具体信息如下:

复制代码
# /usr/local/bin/masterha_master_switch --master_state=alive --conf=/etc/mha/app1.cnf

It is better to execute FLUSH NO_WRITE_TO_BINLOG TABLES on the master before switching. Is it ok to execute on 179.179.19.184(179.179.19.184:3306)? (YES/no): y

Tue Nov 19 17:19:09 2018 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
Tue Nov 19 17:19:09 2018 - [info]  ok.
Tue Nov 19 17:19:09 2018 - [info] Checking MHA is not monitoring or doing failover..
Tue Nov 19 17:19:09 2018 - [info] Checking replication health on 179.179.19.185..
Tue Nov 19 17:19:09 2018 - [info]  ok.
Tue Nov 19 17:19:09 2018 - [error][/usr/local/share/perl5/MHA/MasterRotate.pm, ln161] We should not start online master switch when one of connections are running long updates on the current master(179.179.19.184(179.179.19.184:3306)). Currently 1 update thread(s) are running.
Details:
{'Time' => '12815','db' => undef,'Id' => '1','User' => 'event_scheduler','State' => 'Waiting on empty queue','Command' => 'Daemon','Info' => undef,'Host' => 'localhost'}
Tue Nov 19 17:19:09 2018 - [error][/usr/local/share/perl5/MHA/ManagerUtil.pm, ln177] Got ERROR:  at /usr/local/bin/masterha_master_switch line 53.
复制代码

分析:set global event_scheduler=off; 主从都要关闭

(13). masterha_master_switch 在线切换方面 报错 Got Error: DBI .....failed: Access denied for user

复制代码
# /usr/local/bin/masterha_master_switch --master_state=alive --conf=/etc/mha/app1.cnf

Starting master switch from 179.179.19.185(179.179.19:3306) to 179.179.19.184(179.179.19.184:3306)? (yes/NO): yes

Tue Nov 19 18:52:04 2018 - [info] Checking whether 179.179.19.184(179.179.19.184:3306) is ok for the new master..
Tue Nov 19 18:52:04 2018 - [info]  ok.
Tue Nov 19 18:52:04 2018 - [info] ** Phase 1: Configuration Check Phase completed.
Tue Nov 19 18:52:04 2018 - [info] 
Tue Nov 19 18:52:04 2018 - [info] * Phase 2: Rejecting updates Phase..
Tue Nov 19 18:52:04 2018 - [info] 
Tue Nov 19 18:52:04 2018 - [info] Executing master ip online change script to disable write on the current master:
Tue Nov 19 18:52:04 2018 - [info]   /usr/local/bin/master_ip_online_change_appuanalysis --command=stop --orig_master_host=179.179.19.185 --orig_master_ip=179.179.19.185 --orig_master_port=3306--orig_master_user='weixinLX391P_xldbmha' --orig_master_password='weixinLX391P_xldbmha\)qlk' --new_master_host=179.179.19.184 --new_master_ip=179.179.19.184 --new_master_port=55988 --new_master_user='us_mha' --new_master_password='weixinLX391P_xldbmha\)qlk' --orig_master_ssh_user=root --new_master_ssh_user=root  
Got Error: DBI connect(';host=179.179.19.184;port=3306;mysql_connect_timeout=4','weixinLX391P_xldbmha',...) failed: Access denied for user 'weixinLX391P_xldbmha'@'179.179.19.166' (using password: YES) at /usr/local/share/perl5/MHA/DBHelper.pm line 205.
 at /usr/local/bin/master_ip_online_change_app1 line 119.

Tue Nov 19 18:52:04 2018 - [error][/usr/local/share/perl5/MHA/ManagerUtil.pm, ln177] Got ERROR:  at /usr/local/bin/masterha_master_switch line 53.
复制代码

分析:账号密码有需要转移字符的。app1.cnf 文件中user账号相应的密码 password 不能有待转移的字符,例如本例中的')',但是 账号 repl_user 相应的密码repl_password 没有此限制。

(14).如果使用的是xtrabackup,注意从节点会把event还原上去,可能会造成数据不一致,同步失败的问题。

如果主节点有event,需要手动关闭从节点的event。例如,主节点有归档删除数据的event,从节点需要关闭,否则报错。类似如下错误:

Could not execute Delete_rows event on table ????DB.*****table; Can't find record in '*****', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log FIRST, end_log_pos XXXXXXX

 

(15) .GTID 模式转换为传统模式后,MHA 机制下数据库主从检查报错。

检查的命令

1
/usr/local/bin/masterha_check_repl --conf=/etc/mha/qqweixinordb.cnf

主要的报错信息

1
2
3
4
5
6
7
8
9
10
Can't exec "mysqlbinlog"No such file or directory at /usr/local/share/perl5/MHA/BinlogManager.pm line 106.
mysqlbinlog version command failed with rc 1:0, please verify PATH, LD_LIBRARY_PATH, and client options
 at /usr/local/bin/apply_diff_relay_logs line 493.
Fri Aug 28 04:38:22 2019 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln205] Slaves settings check failed!
Fri Aug 28 04:38:22 2019 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln413] Slave configuration failed.
Fri Aug 28 04:38:22 2019 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln424] Error happened on checking configurations.  at /usr/local/bin/masterha_check_repl line 48.
Fri Aug 28 04:38:22 2019 - [error][/usr/local/share/perl5/MHA/MasterMonitor.pm, ln523] Error happened on monitoring servers.
Fri Aug 28 04:38:22 2019 - [info] Got exit code 1 (Not master dead).
 
MySQL Replication Health is NOT OK!

解决方案--在每个DB节点执行以下命令

1
2
3
ln -s /usr/local/mysql/bin/mysqlbinlog /usr/local/bin/mysqlbinlog
 
ln -s /usr/local/mysql/bin/mysql /usr/local/bin/mysql

 再次检查,报错信息消失,OK。 

(16).root 账号密码过期

以root账号设置的ssh免密登陆,而ssh有过期限制,则mha ssh检查时报错:

复制代码
/usr/local/bin/masterha_check_ssh --conf=/etc/mha/qqorder.cnf
Thu Nov  5 10:09:09 2018 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Thu Nov  5 10:09:09 2018 - [info] Reading application default configuration from /etc/mha/pms20epime.cnf..
Thu Nov  5 10:09:09 2018 - [info] Reading server configuration from /etc/mha/pms20epime.cnf..
Thu Nov  5 10:09:09 2018 - [info] Starting SSH connection tests..
Thu Nov  5 10:09:09 2018 - [error][/usr/local/share/perl5/MHA/SSHCheck.pm, ln63]
Thu Nov  5 10:09:09 2018 - [debug]  Connecting via SSH from root@172.181.191.191(172.181.191.191:22) to root@172.181.191.192(172.181.191.192:22)..

WARNING: Your password has expired.
Password change required but no TTY available.
Thu Nov  5 10:09:09 2018 - [error][/usr/local/share/perl5/MHA/SSHCheck.pm, ln111] SSH connection from root@172.181.191.191(172.181.191.191:22) to root@172.181.191.192(172.181.191.192:22) failed!
Thu Nov  5 10:09:10 2018 - [error][/usr/local/share/perl5/MHA/SSHCheck.pm, ln63]
Thu Nov  5 10:09:09 2018 - [debug]  Connecting via SSH from root@172.181.191.192(172.181.191.192:22) to root@172.181.191.191(172.181.191.191:22)..

WARNING: Your password has expired.
Password change required but no TTY available.
Thu Nov  5 10:09:10 2018 - [error][/usr/local/share/perl5/MHA/SSHCheck.pm, ln111] SSH connection from root@172.181.191.192(172.181.191.192:22) to root@172.181.191.191(172.181.191.191:22) failed!
SSH Configuration Check Failed!
 at /usr/local/bin/masterha_check_ssh line 44.
复制代码

另外一种表现,就是第二次执行账号切换时报错  sudo su -

 

解决方案在root账号下,执行以下命令:

chage -M 99999 root

 

 

参考文献

参考:mysql5.7.24 GTID+半同步 1主3从

参考:一步一个坑搭建MHA http://www.ttlsa.com/mysql/step-one-by-one-deploy-mysql-mha-cluster/

参考:MHA官网 https://code.google.com/p/mysql-master-ha/

参考:比较详细的MHA部署搭建:https://blog.csdn.net/qq_35209838/article/details/86497864

软件下载:https://cbs.centos.org/koji/buildinfo?buildID=1261

安装参考:https://www.cnblogs.com/winstom/p/11022014.html

概念:高可用架构方案  中的【3】MHA

mysql故障应用参考:https://cloud.tencent.com/developer/article/1339797

mha binlogserver:https://www.cnblogs.com/tim1blog/p/9877164.html

mysql8.0使用 MHA:https://blog.csdn.net/wangbin9536/article/details/104157646

posted @ 2019-10-14 21:56  郭大侠1  阅读(613)  评论(0编辑  收藏  举报