一、简介
MHA能够在较短的时间内实现自动故障检测和故障转移,通常在10-30秒以内。在主从框架中,MHA能够很好地解决主从复制过程中的数据一致性问题,由于不需要在现有的主从框架中添加额外的服务器,仅需要一个manager节点,而一个Manager能管理多套复制,所以能大大地节约服务器的数量。另外,安装简单,无性能损耗,以及不需要修改现有的复制部署也是它的优势之处。
MHA可以监控主从复制环境中的主库,并在检测到主库故障时执行自动主库故障转移,当主库出现故障时,它可以自动将最新数据的从库提升为新的主库,然后将所有其它的从库重新指向新的主库。
MHA还提供在线主库切换功能,能够将当前运行的主库安全的切换到一个新的主库(通过将从库提升为主库),能在0.5-2秒内完成,在切换过程中仅阻止写入。
官方文档:https://github.com/yoshinorim/mha4mysql-manager/wiki/Overview
下载地址:https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads
二、体系结构
MHA由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上管理多个Master-Slave集群,也可以部署在一台Slave上。
Manager组件结构:
• masterha_manger 启动MHA
• masterha_check_ssh 检查MHA的SSH配置状况
• masterha_check_repl 检查MySQL复制状况
• masterha_master_monitor 检测master是否宕机
• masterha_check_status 检测当前MHA运行状态
• masterha_master_switch 控制故障转移(自动或者手动)
• masterha_conf_host 添加或删除配置的server信息
node 组件结构:
• save_binary_logs 保存和复制master的二进制日志
• apply_diff_relay_logs 识别差异的中继日志事件并将其差异的事件应用于其它的从库
• purge_relay_logs 清除中继日志(不会阻塞SQL线程)
三、故障转移原理
以一主多从为例,如果主库崩溃,MHA则会选择一个数据最新的从库,将其提升为新的主库,然后让其它从库从新的主库开始复制。实际上,这并本不是一件小事。即使可以识别出最新的从库,其他从库也有可能尚未收到所有二进制日志事件。要知道MySQL主从复制是异步的,某些(或没有一个)从库可以从崩溃的主库接收到所有二进制日志事件,也有可能只有一些从库收到了最新事件。
如果就此重新依据新主库构建主从复制,则这些从库将会丢失事务,这将导致数据一致性问题。为了避免这些一致性问题,丢失的binlog事件(尚未到达所有从库)需要被识别,然后在依据新主库上启动复制之前,依次将其应用于每个从库,即从库互相识别差异互补。MHA会通过识别来自最新从库的差异中继日志事件,并将其应用于所有其它从库,包括那些尚未收到最新中继日志事件的从库,来保证所有从库的一致性。
MHA的目标是在没有任何备用服务器的情况下,尽快使主库故障转移和恢复过程完全自动化。恢复包括确定新的主库、识别从库之间的差异中继日志事件、将差异的事件应用于新的主库、同步其它从库并使它们从新的主库开始复制。根据复制延迟,MHA通常可以在10到30秒的停机时间内完成故障转移。
具体流程如下:
1、监控
通过masterha_master_monitor ,每隔 ping_interval 秒探测一次主库心跳。
如果探测不到心跳,一共给4次机会,如果还探测不到就会认为主库宕机。
2、选主
(1)从判断各个从库的日志(position或者GTID)选,最接近主库的从库,成为备选主。
(2)在配置文件中,可以将特定从库指定为候选主库(通过设定权重 candidate_master=1 )如果有则优先按照权重指定为候选主库。注意仅仅只是候选主库而已。
• 默认情况下如果一个从库落后主库 100M的relay logs的话,即使有权重,也没用。
• 如果设定了"check_repl_delay=0"的话,即使落后很多日志,也会被选为候选主库。
(3)如果从库日志数据一致,则按照配置文件顺序选主。
(4)整体来说,选主策略优先级:最新日志+候选主库 > 最新日志 > 候选主库 > 配置文件顺序。
3、日志补偿
(1)如果主库SSH能连接,则从库会对比主库position或者GTID 号,save_binary_logs会立即将二进制日志保存至各个从节点并且应用。
(2)如果主库SSH不能连接,则通过apply_diff_relay_logs对比从库之间的relay log日志的差异。
4、故障转移
取消所有节点的从库状态,构建新的主从关系。自动将故障节点,从配置文件剔除。
5、自动退出
完成故障转移后,manager会自动退出,所以它是一次性的,只能完成一次切换。
完成转移后,我们需要重新启动MHA和及时修复故障节点,因此,我们需要配置邮件通知。
四、MHA搭建
1、节点规划
节点名称 | ip地址 | 角色 |
---|---|---|
db01 | 10.0.0.51 | 主库 |
db02 | 10.0.0.52 | 从库 |
db03 | 10.0.0.53 | 从库 |
2、主从构建
主库db01操作:
(1)开启binlog日志
[root@db01 ~]# vim/etc/my.cnf # 在mysqld下添加以下配置
server_id=1
log_bin=/service/data/binlog/mysql-bin
(2)主库创建专门用于复制的用户
mysql> grant replication slave on *.* to repl@'10.0.0.%' identified by '000000';
从库db02、db03操作:主从时间要同步
1、修改配置文件
[root@db02 ~]# vim/etc/my.cnf # 在mysqld下添加以下配置
server_id=2
log_bin=/service/data/binlog/mysql-bin
2、配置从库(因为没啥数据,也不关注太多,直接从头开始复制)
mysql> change master to
master_host='10.0.0.51',
master_user='repl',
master_password='000000',
master_port=3306,
master_log_file='mysql-bin.000001',
master_log_pos=1;
4、启动从库的复制功能
mysql> start slave;
5、检查从库复制功能状态
mysql> show slave status \G
......
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
......
3、安装软件
所有节点安装node
[root@db01 ~]# yum install perl-DBD-MySQL -y
[root@db01 ~]# rpm -vih mha4mysql-node-0.56-0.el6.noarch.rpm
db03节点安装manger
[root@db03 ~]# yum install -y epel-release perl-Config-Tiny perl-Time-HiRes
[root@db03 ~]# yum install -y perl-Log-Dispatch perl-Parallel-ForkManager
[root@db03 ~]# rpm -ivh mha4mysql-manager*.rpm
4、各节点准备工作
(1)各节点配置关键程序软链接(MHA会去使用以下链接的全路径去调用,代码写死的所以需要做软链接)
[root@db01 ~]# ln -s /service/app/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog
[root@db01 ~]# ln -s /service/app/mysql/bin/mysql /usr/bin/mysql
(2)配置互信(免密ssh)
各节点:
[root@db01 ~]# sed -i '/StrictHostKeyChecking/c StrictHostKeyChecking no' /etc/ssh/ssh_config
db01节点:
[root@db01 ~]# rm -rf /root/.ssh
[root@db01 ~]# ssh-keygen
[root@db01 ~]# cd /root/.ssh
[root@db01 ~]# mv id_rsa.pub authorized_keys
[root@db01 ~]# scp -r /root/.ssh 10.0.0.52:/root
[root@db01 ~]# scp -r /root/.ssh 10.0.0.53:/root
(3)各节点验证
[root@db01 ~]# ssh 10.0.0.51 hostname
db01
[root@db01 ~]# ssh 10.0.0.52 hostname
db02
[root@db01 ~]# ssh 10.0.0.53 hostname
db03
5、在db01主库中创建mha管理用户
mysql> grant all privileges on *.* to mha@'10.0.0.%' identified by 'mha';
6、配置文件准备(db03)
(1)创建配置文件目录和日志目录
[root@db03 ~]# mkdir -p /etc/mha
[root@db03 ~]# mkdir -p /var/log/mha/app1
(2)编辑mha配置文件
[root@db03 ~]# vim /etc/mha/app1.cnf
[server default]
user=mha # MHA管理用户
password=mha # MHA管理用户密码
manager_workdir=/var/log/mha/app1 # 工作目录
manager_log=/var/log/mha/app1/app1.log # 日志位置
master_binlog_dir=/service/data/binlog/ # 主库binlog目录
ping_interval=1 # 探测主库存货的间隔时间
repl_user=repl # 主从复制管理用户
repl_password=000000 # 主从复制管理用户密码
ssh_user=root # ssh连接用户
[server1]
hostname=10.0.0.51
port=3306
[server2]
hostname=10.0.0.52
port=3306
[server3]
hostname=10.0.0.53
port=3306
7、状态检查
(1)互信检查
[root@db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
(2)主从状态检查
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
8、开启MHA
[root@db03 ~]# 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 &
命令参数说明:
nohup 后台启动
masterha_manager MHA启动程序
--conf=/service/mha/app1.cnf 指定配置文件
--remove_dead_master_conf 移除崩溃的主库节点配置
--ignore_last_failover 忽略最后一次切换
安全机制说明:
1.完成一次切换后,会生成一个锁文件。
2.再次进行切换时,会检查锁文件。
3.如果锁文件存在,则不能进行切换,8小时后才能进行再次切换。
4.所以配置--ignore_last_failover进行跳过。
9、查看MHA状态
[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:11077) is running(0:PING_OK), master:10.0.0.51
10、建议添加配置
以上MHA环境已经构建完成,以下是建议配置的mysql配置:
(1)关闭自动删除relay log的功能
relay_log_purge=0
(2)从库配置只读
read_only=1
(3)从库开启保存binlog
log_slave_updates
五、故障转移演示
1、关闭db01数据库模拟故障
[root@db01 ~]# systemctl stop mysqld
2、db03查看manager日志
[root@db03 ~]# vim /var/log/mha/app1/app1.log
......
Started automated(non-interactive) failover.
The latest slave 10.0.0.52(10.0.0.52:3306) has all relay logs for recovery.
Selected 10.0.0.52(10.0.0.52:3306) as a new master.
10.0.0.52(10.0.0.52:3306): OK: Applying all logs succeeded.
10.0.0.53(10.0.0.53:3306): This host has the latest relay log events.
Generating relay diff files from the latest slave succeeded.
10.0.0.53(10.0.0.53:3306): OK: Applying all logs succeeded. Slave started, replicating from 10.0.0.52(10.0.0.52:3306)
10.0.0.52(10.0.0.52:3306): Resetting slave info succeeded.
Master failover to 10.0.0.52(10.0.0.52:3306) completed successfully.
通过日志发现主库已经切换到了db02
3、db03查看从库状态
mysql> show slave status \G
......
Master_Host: 10.0.0.52
Master_User: repl
Master_Port: 3306
......
节点修复重新构建主从
[root@db01 ~]# systemctl start mysqld
mysql> change master to
master_host='10.0.0.52',
master_user='repl',
master_password='000000',
master_port=3306,
master_log_file='mysql-bin.000001',
master_log_pos=1;
mysql> start slave;
mysql> show slave status \G
......
Master_Host: 10.0.0.52
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
......
重新部署MHA
1、将db01节点重新加入MHA环境
[root@db01 ~]# vim /etc/mha/app1.cnf # 添加
[server1]
hostname=10.0.0.51
port=3306
2、重新检查状态
[root@db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
3、重新启动MHA
[root@db03 ~]# 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 &
4、检查
[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:11089) is running(0:PING_OK), master:10.0.0.52
六、VIP
前面我们进行主库故障转移演示,主库成功由10.0.0.51切换到了10.0.0.52。那么大家想到没,对于连接数据库的应用来而言该怎么办呢?因为主库ip地址的变化,为了能让应用正常服务,需要修改代码重新上线。那么这显然不是我们所期望,因此MHA也给我们提供了VIP地址漂移的功能。应用绑定VIP,VIP处于主库,当主库宕机了,主库切换到新库,VIP跟着漂移到新库,对于应用而言还是连接着主库。
对于VIP,MHA是通过调用脚本实现的,官方给我们提供了脚本。下载地址:https://github.com/yoshinorim/mha4mysql-manager/archive/master.zip,脚本位于下载文件中的samples/scripts中,注意官方提供的脚本只是个模板,是不能直接用的,需要进行修改。
以下提供修改后的几个脚本下载地址:https://www.cpweb.top/wp-content/uploads/2020/11/scripts.zip
1、下载脚本
[root@db03 ~]# https://www.cpweb.top/wp-content/uploads/2020/11/scripts.zip
[root@db03 ~]# unzip scripts.zip -d /usr/local/bin/
[root@db03 ~]# chmod +x /usr/local/bin/*
2、根据服务器自身情况修改脚本
[root@db03 bin]# cd /usr/local/bin/
[root@db03 bin]# vim master_ip_failover # 修改以下配置
my $vip = '10.0.0.50/24'; # VIP地址
my $key = '1'; # 网卡后面数字
my $if = 'ens33'; # 指定VIP绑定网卡
my $ssh_start_vip = "/sbin/ifconfig $if:$key $vip"; # 生成VIP
my $ssh_stop_vip = "/sbin/ifconfig $if:$key down"; # 关闭VIP
my $ssh_Bcast_arp= "/sbin/arping -I $if -c 3 -A 10.0.0.50"; # arp广播
3、修改manager配置文件
[root@db03 ~]# vim /etc/mha/app1.cnf # 在[server default]下添加以下配置
master_ip_failover_script=/usr/local/bin/master_ip_failover
4、重启MHA
[root@db03 ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
[root@db03 ~]# 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 &
5、主库手工添加VIP
[root@db02 ~]# ifconfig ens33:1 10.0.0.50/24
模拟故障,测试VIP地址漂移
1、主库模拟故障
[root@db02 ~]# systemctl stop mysqld
2、db03查看manager日志
Started automated(non-interactive) failover.
Invalidated master IP address on 10.0.0.52(10.0.0.52:3306)
The latest slave 10.0.0.51(10.0.0.51:3306) has all relay logs for recovery.
Selected 10.0.0.51(10.0.0.51:3306) as a new master.
10.0.0.51(10.0.0.51:3306): OK: Applying all logs succeeded.
10.0.0.51(10.0.0.51:3306): OK: Activated master IP address.
10.0.0.53(10.0.0.53:3306): This host has the latest relay log events.
Generating relay diff files from the latest slave succeeded.
10.0.0.53(10.0.0.53:3306): OK: Applying all logs succeeded. Slave started, replicating from 10.0.0.51(10.0.0.51:3306)
10.0.0.51(10.0.0.51:3306): Resetting slave info succeeded.
Master failover to 10.0.0.51(10.0.0.51:3306) completed successfully.
3、db01查看VIP
[root@db01 bin]# ip a show ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:67:84:79 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.51/24 brd 10.0.0.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet 10.0.0.50/24 brd 10.0.0.255 scope global secondary ens33:1
valid_lft forever preferred_lft forever
inet6 fe80::3b3e:c579:6d22:26ef/64 scope link noprefixroute
valid_lft forever preferred_lft forever
4、db03查看从库状态
mysql> show slave status \G
......
Master_Host: 10.0.0.51
......
七、邮件通知故障转移
因为MHA在完成故障转移后,manager会自动退出,为了重新去启动MHA和及时修复故障节点,我们需要去配置邮件通知。同样MHA通过调用脚本实现,脚本在上面下载的scripts包中。
1、修改脚本
[root@db03 bin]# vim send_report # 根据情况修改以下配置
my $smtp='smtp.qq.com'; # smtp服务器
my $mail_from='xx@qq.com'; # 发件箱
my $mail_user='xx'; # 用户名
my $mail_pass='xx'; # 授权码
my $mail_to=['xx@qq.com']; # 收件箱
2、修改manager配置文件
[root@db03 ~]# vim /etc/mha/app1.cnf # 在[server default]下添加以下配置
report_script=/usr/local/bin/send_report
3、重启MHA
[root@db03 ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
[root@db03 ~]# 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 &
模拟故障,收到邮件提醒:
八、binlog_server
前面我们说到MySQL主从复制是异步的,当主库崩溃后,某些(或没有一个)从库可以从崩溃的主库接收到所有二进制日志事件,也有可能只有一些从库收到了最新事件。如果再这这种情况下,SSH不能连接到主库,那么从库只会通过apply_diff_relay_logs对比从库之间的relay log日志的差异进行互补。那么肯定会缺少部分数据,binlog_server 就是为了防止这样情况发生的,
顾名思义binlog_server,即一台专门储存主库binlog日志文件的节点。当主库故障,从库SSH不能连接到主库时,从库会去这个节点去请求最新数据。为了方便下面我们将它部署在db03。
1、创建目录
[root@db03 ~]# mkdir -p /binlog_server
[root@db03 ~]# chown -R mysql.mysql /binlog_server
[root@db03 ~]# cd /binlog_server/
2、使用mysqlbinlog远程实时备份主库binlog
[root@db03 binlog_server]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:9097) is running(0:PING_OK), master:10.0.0.51
[root@db03 binlog_server]# mysqlbinlog -R --host=10.0.0.51 --user=mha --password=mha --raw --stop-never mysql-bin.000001 &
3、修改manager配置文件
[root@db03 ~]# vim /etc/mha/app1.cnf # 添加
[binlog1]
no_master=1
hostname=10.0.0.53
master_binlog_dir=/binlog_server/
4、重启MHA
[root@db03 ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
[root@db03 ~]# 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 &
九、主库在线切换
MHA给我们提供了在线切换主库的方法,通过masterha_master_switch 手动切换实现。主要有以下两种方式:
1、不调用脚本进行在线切换(不推荐)
[root@db03 ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_master_switch --conf=/etc/mha/app1.cnf --master_state=alive --new_master_host=10.0.0.52 --orig_master_is_new_slave --running_updates_limit=10000
参数说明:
--master_state=alive: 在主库存活情况下切换
--new_master_host=xx: 指定新主库
--orig_master_is_new_slave: 原来主库作为新主库
--running_updates_limit=xx: 如果延迟超过xx毫秒,切换失败
注意:执行会收到以下问询和提示,意思是没有定义脚本,如果不手动在当前主库禁止写入,应用程序会继续写入数据。当然还有其它问询,自行填写。
master_ip_online_change_script is not defined. If you do not disable writes on the current master manually, applications keep writing on the current master. Is it ok to proceed? (yes/NO): yes
此种方法切换注意事项:
(1)原主库加FTWRL,否则会造成主从不一致。
(2)需要手工切换vip,脚本可以随着主库切换自动切换。
(3)发邮件功能无效
2、调用脚本进行在线切换(推荐)
(1)根据情况对脚本进行修改
[root@db03 ~]# vim /usr/local/bin/master_ip_online_change
my $vip = "10.0.0.50/24";
my $key = "1";
my $ssh_start_vip = "/sbin/ifconfig ens33:$key $vip";
my $ssh_stop_vip = "/sbin/ifconfig ens33:$key $vip down";
my $ssh_Bcast_arp= "/sbin/arping -I ens33 -c 3 -A 10.0.0.50";
2、修改配置文件
[root@db03 ~]# vim /etc/mha/app1.cnf # 在[server default]下添加以下配置
master_ip_online_change_script=/usr/local/bin/master_ip_online_change
3、停止MHA
[root@db03 ~]# masterha_stop --conf=/etc/mha/app1.cnf
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
4、在线切换
[root@db03 ~]# masterha_master_switch --conf=/etc/mha/app1.cnf --master_state=alive --new_master_host=10.0.0.51 --orig_master_is_new_slave --running_updates_limit=10000
参考文章:
https://www.jianshu.com/p/0f7b5a962ba7
https://github.com/yoshinorim/mha4mysql-manager/wiki/Overview