zoukankan      html  css  js  c++  java
  • (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不能一样

    #采用命令方式将从库设为只读,不要将该参数写进配置文件中
    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 statusG

    (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
    [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

    #在主库上,手动添加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 "
    
    IN SCRIPT TEST====$ssh_stop_vip==$ssh_start_vip===
    
    ";
    
        if ( $command eq "stop" || $command eq "stopssh" ) {
    
            my $exit_code = 1;
            eval {
                print "Disabling the VIP on old master: $orig_master_host 
    ";
                &stop_vip();
                $exit_code = 0;
            };
            if ($@) {
                warn "Got Error: $@
    ";
                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 
    ";
                &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 
    ";
            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
    ";
    }
    【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.
    ";
          }
          else {
            die "Failed!
    ";
          }
          $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..
    ";
          #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)
    ",
                current_time_us(), $#threads + 1, $time_until_read_only * 100;
              if ( $#threads < 5 ) {
                print Data::Dumper->new( [$_] )->Indent(0)->Terse(1)->Dump . "
    "
                  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.
    ";
          }
          else {
            die "Failed!
    ";
          }
    
          ## 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)
    ",
                current_time_us(), $#threads + 1, $time_until_kill_threads * 100;
              if ( $#threads < 5 ) {
                print Data::Dumper->new( [$_] )->Indent(0)->Terse(1)->Dump . "
    "
                  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 
    ";
                    &stop_vip();     
    
    
          ## Terminating all threads
          print current_time_us() . " Killing all application threads..
    ";
          $orig_master_handler->kill_threads(@threads) if ( $#threads >= 0 );
          print current_time_us() . " done.
    ";
          #$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: $@
    ";
          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.
    ";
          $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..
    ";
          #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 
    ";
                    &start_vip();
                    $exit_code = 0;
        };
        if ($@) {
          warn "Got Error: $@
    ";
          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
    ";
      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 "
    
    
    ****************************
    "
        echo -e "Disabled thi VIP - $vip on old master: $orig_master_host 
    "
        $stop_vip
        if [ $? -eq 0 ]
          then
        echo "Disabled the VIP successfully"
          else
        echo "Disabled the VIP failed"
        fi
        echo -e "***************************
    
    
    "
      fi
    
    if [ $command = 'start' -o $command = 'status' ]
      then
        echo -e "
    
    
    *************************
    "
        echo -e "Enabling the VIP - $vip on new master: $new_master_host 
    "
        $start_vip
        if [ $? -eq 0 ]
          then
        echo "Enabled the VIP successfully"
          else
        echo "Enabled the VIP failed"
        fi
        echo -e "***************************
    
    
    "
    fi
    【4.5.3】授权
    chmod +x /usr/local/bin/master_ip_failover
    chmod +x /usr/local/bin/master_ip_online_change

    【4.5.4】keepalive(待写)

    【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库,进行复制

    【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

    对于 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)复制检查

     附录:故障解决

    (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)

    参考文献

    参考: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

  • 相关阅读:
    《最后期限》阅读笔记03
    《最后期限》阅读笔记02
    《最后期限》阅读笔记01
    返回一个二维整数数组中最大联通子数组的和
    软件工程团队开发——第一次冲刺会议总结
    结对项目开发电梯调度
    软件工程课程建议
    第二次冲刺07
    第二次冲刺06
    第二次冲刺05
  • 原文地址:https://www.cnblogs.com/gered/p/11674463.html
Copyright © 2011-2022 走看看