DBA专题
DBA授课
DBA公开课
DBA训练营三天
01.Mysql基础入门-数据库简介
02.Mysql基础入门-部署与管理体系
03.MySQL主流版本版本特性与部署安装
04.Mysql-基础入门-用户与权限
05 MySQL-SQL基础2
06 SQL高级开发-函数
07 MySQL-SQL高级处理
08 SQL练习 作业
09 数据库高级开发2
10 Mysql基础入门-索引
11 Mysql之InnoDB引擎架构与体系结构
12 Mysql之InnoDB存储引擎
13 Mysql之日志管理
14 Mysql备份,恢复与迁移
15 主从复制的作用及重要性
16 Mysql Binlog Event详解
17 Mysql 主从复制
18 MySQL主从复制延时优化及监控故障处理
19 MySQL主从复制企业级场景解析
20 MySql主从复制搭建
21 MySQL高可用-技术方案选型
22 MySQL高可用-MHA(原理篇)
23 MySQL MHA实验
24 MySQL MGR
25 部署MySQL InnoDB Cluster
26 MySQL Cluster(MGR)
27 MySQL ProxySQL中间件
相信可能就有无限可能
-
+
首页
22 MySQL高可用-MHA(原理篇)
# 1.MHA简介 ## 1.1.基本概念 ```bash 1.MHA(Master HA)是一款开源的 MySQL 的高可用程序,Perl语言 2.原理:Mater故障,MHA选取数据最新的slave节点成为master节点。 3.功能: - 10-30s实现master failover(9-12s可以检测到主机故障,7-10s可以关闭主机,在用很短的时间应用差异日志) - 部署简单,无需对现有M-S结构做任何改动(至少3台,保证切换后仍保持M-S结构) - 支持手动在线切换(主机硬件维护),downtime几乎很短0.5-2s - 保证故障切换后多从库数据的一致性 - 完全自动化的failover及快速复制架构恢复方案(一主多从) - 恢复过程包括:选择新主库、确认从库间relay log差异、新主库应用必要events、其他从库同步差异语句、重新建立复制连接 ``` ## 1.2.服务介绍 ```bash MHA两种角色,MHA Manager(管理节点)和 MHA Node(数据节点) MHA Manager: - 管理节点,发送指令。部署在一台独立节点上管理一个MySQL主从集群(可以管理多个,但实际生产一个MHA与Mysql 1:1关系) - 其他的帮助脚本运行:手动切换master;master/slave状态检测 - master自动切换及故障转移命令运行 MHA Node: - 执行节点,接受指令.监控具备解析和清理logs功能的脚本来加快故障转移 - 复制主节点的binlog数据 - 对比从节点的中继日志文件 - 无需停止从节点的SQL线程,定时删除中继日志 ``` - 核心源码原理及功能 ```bash #1.核心功能 监控:4次心跳监控master,超过则默认宕机开始选主 选主:4个数组+5个策略 4个数组:slave节点房间一个数组中维护元信息 - Alive_slaves:所有存活且sql_thread监控从节点 - Latest:选举relog log最新的server,如果多个从节点,则Read_master_log_pos与master_log_file一定相等 - Perf:candidate_master配置的slave - Bad: 1.检测有故障的server 2.MHA 配置文件设置了 no_master 的 server 3.log_bin 没有打开的server 4.版本不兼容的server 5.复制落后太多的server 5个策略:先级为1->6 (1) 指定主库切换的,优先级最高(通常这是在手动切换) (2) 如果server 在 latest 数组中,且在 perf 数组中,则优先返回 (3) 如果server 在 alive_servers 数组中,且在 perf 数组中,则优先返回 (4) 如果server 在 lastest 中,则优先返回 (5) 如果server 在 alive_servers 中,则返回 (6) 否则选举失败 ``` ![Untitled (35)](https://img.sunrisenan.com/img/2024/04/27/144040352.png) - MHA-架构图 ![Untitled (36)](https://img.sunrisenan.com/img/2024/04/27/144130220.png) # [2.](http://2.ss)MHA搭建,结构,参数 ## 2.1.MHA基础搭建 ```bash #规划 master01:172.21.188.37 node mha-manager slave01 :172.21.188.36 node slave02 :172.21.188.19 node #1.初始环境准备 ##1.1.mysql搭建 ###主库搭建 mkdir -p /data/mysql3306/{log,etc,tmp,data} vim /data/mysql3306/etc/my.cnf [client] port = 3306 socket = /data/mysql3306/tmp/mysql.sock default_character_set = utf8mb4 [mysqld] user= mysql port = 3306 server_id = 1101 datadir = /data/mysql3306/data socket = /data/mysql3306/tmp/mysql.sock log-error = /data/mysql3306/log/mysql.err pid-file = /data/mysql3306/data/mysql.pid tmpdir = /data/mysql3306/tmp/ log_bin = /data/mysql3306/data/mysql-bin #replication binlog_format = row binlog_row_image = full expire_logs_days = 10 relay-log = /data/mysql3306/data/relay-log slave_net_timeout = 30 #skip-slave-start slave-parallel-workers = 0 relay_log_info_repository = TABLE master_info_repository = TABLE sync_binlog = 1 gtid_mode = ON enforce-gtid-consistency = ON log-slave-updates = ON #初始化账号与启动mysql touch /data/mysql3306/log/mysql.err chown -R mysql.mysql /data/mysql3306/ ln -s /chj/class/dowland/mysql-8.0.24/ /usr/local/mysql mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql3306/data mysqld --defaults-file=/data/mysql3306/etc/my.cnf --user=mysql & mysql -uroot -p -S /data/mysql3306/tmp/mysql.sock create user qianlong@'%' identified by '123456'; grant all on *.* to qianlong@'%' with grant option; mysql -uqianlong -p123456 -h172.21.188.37 -P3306 #创建账户 create user repl@'%' identified with mysql_native_password by '123456';grant replication slave on *.* to repl@'%'; ###从库01搭建 mkdir -p /data/mysql3306/{log,etc,tmp,data} vim /data/mysql3306/etc/my.cnf [client] port = 3306 socket = /data/mysql3306/tmp/mysql.sock default_character_set = utf8mb4 [mysqld] user= mysql port = 3306 server_id = 1102 datadir = /data/mysql3306/data socket = /data/mysql3306/tmp/mysql.sock log-error = /data/mysql3306/log/mysql.err pid-file = /data/mysql3306/data/mysql.pid tmpdir = /data/mysql3306/tmp/ log_bin = /data/mysql3306/data/mysql-bin #replication binlog_format = row binlog_row_image = full expire_logs_days = 10 relay-log = /data/mysql3306/data/relay-log slave_net_timeout = 30 #skip-slave-start slave-parallel-workers = 0 relay_log_info_repository = TABLE master_info_repository = TABLE sync_binlog = 1 #gtid gtid_mode = ON enforce-gtid-consistency = ON log-slave-updates = ON #初始化账号与启动mysql touch /data/mysql3306/log/mysql.err chown -R mysql.mysql /data/mysql3306/ ln -s /chj/class/dowland/mysql-8.0.24/ /usr/local/mysql mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql3306/data mysqld_safe --defaults-file=/data/mysql3306/etc/my.cnf --user=mysql & mysql -uroot -p -S /data/mysql3306/tmp/mysql.sock create user qianlong@'%' identified by '123456'; grant all on *.* to qianlong@'%' with grant option; mysql -uqianlong -p123456 -h172.21.188.36 -P3306 #创建账户 create user repl@'%' identified with mysql_native_password by '123456';grant replication slave on *.* to repl@'%'; ###从库02搭建 mkdir -p /data/mysql3306/{log,etc,tmp,data} vim /data/mysql3306/etc/my.cnf [client] port = 3306 socket = /data/mysql3306/tmp/mysql.sock default_character_set = utf8mb4 [mysqld] user= mysql port = 3306 server_id = 1103 datadir = /data/mysql3306/data socket = /data/mysql3306/tmp/mysql.sock log-error = /data/mysql3306/log/mysql.err pid-file = /data/mysql3306/data/mysql.pid tmpdir = /data/mysql3306/tmp/ log_bin = /data/mysql3306/data/mysql-bin #replication binlog_format = row binlog_row_image = full expire_logs_days = 10 relay-log = /data/mysql3306/data/relay-log slave_net_timeout = 30 #skip-slave-start slave-parallel-workers = 0 relay_log_info_repository = TABLE master_info_repository = TABLE sync_binlog = 1 #gtid gtid_mode = ON enforce-gtid-consistency = ON log-slave-updates = ON #初始化账号与启动mysql touch /data/mysql3306/log/mysql.err chown -R mysql.mysql /data/mysql3306/ ln -s /chj/class/dowland/mysql-8.0.24/ /usr/local/mysql mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql3306/data mysqld_safe --defaults-file=/data/mysql3306/etc/my.cnf --user=mysql & mysql -uroot -p -S /data/mysql3306/tmp/mysql.sock create user qianlong@'%' identified by '123456'; grant all on *.* to qianlong@'%' with grant option; mysql -uqianlong -p123456 -h172.21.188.19 -P3306 #创建账户 create user repl@'%' identified with mysql_native_password by '123456';grant replication slave on *.* to repl@'%'; ## 1.2.搭建gtid主从 #master01查看GTID点位 show variables like '%gtid%'; 7519a193-1b60-11ed-a3dd-fa202013137b:1-2 slave01,slave02分别执行 reset master; SET @@GLOBAL.GTID_PURGED ="7519a193-1b60-11ed-a3dd-fa202013137b:1-2"; change master to master_host='172.21.188.37', MASTER_PORT=3306, master_user='qianlong', master_password='123456' , MASTER_AUTO_POSITION=1; start slave; show slave status\G ## 1.3.搭建MHA #配置SSH互信 rm -rf /root/.ssh ssh-keygen cd /root/.ssh cp id_rsa.pub authorized_keys scp -r /root/.ssh 172.21.188.37:/root scp -r /root/.ssh 172.21.188.19:/root #安装mha https://github.com/yoshinorim/mha4mysql-manager/wiki/Downloads yum install perl-DBD-MySQL -y rpm -ivh mha4mysql-node*.rpm #主节点master01创建mha用户和安装mha管理节点 create user mha@'172.21.%' identified with mysql_native_password by 'mha'; grant all privileges on *.* to mha@'172.21.%'; yum install -y perl-Config-Tiny epel-release perl-Log-Dispatch perl-Parallel-ForkManager perl-Time-HiRes rpm -ivh mha4mysql-manager*.rpm #配置文件准备 mkdir -p /data/mha/mysql3306 vim /data/mha/mha_3306.cnf [server default] manager_log=/data/mha/mysql3306/manager.log manager_workdir=/data/mha/mysql3306/manager master_binlog_dir=/data/mha/mysql3306/ user=mha password=mha ping_interval=2 repl_password=123456 repl_user=qianlong ssh_user=root [server1] hostname=172.21.188.37 port=3306 [server2] hostname=172.21.188.19 candidate_master=1 port=3306 [server3] hostname=172.21.188.36 port=3306 #前置互信与主从状态检查 masterha_check_ssh --conf=/data/mha/mha_3306.cnf masterha_check_repl --conf=/data/mha/mha_3306.cnf # 开启MHA(master节点): nohup masterha_manager --conf=/data/mha/mha_3306.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /data/mha/mysql3306/manager.log 2>&1 & # 查看MHA状态 masterha_check_status --conf=/data/mha/mha_3306.cnf ``` ## 2.2.MHA结构 ```bash 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 组件(MHAManager的脚本触发,无需人手操作) - save_binary_logs 保存和复制master的二进制日志 - apply_diff_relay_logs 识别差异的中继日志事件并将其差异的事件应用于其他的slave - purge_relay_logs 清除中继日志(不会阻塞SQL线程)-需要命令挂起 自定义组件 - secondary_check_script:通过多条网络路由检测master的可用性; - master_ip_failover_script:更新application使用的masterip; - shutdown_script:强制关闭master节点; - report_script:发送报告; - init_conf_load_script:加载初始配置参数; - master_ip_online_change-script:更新master节点ip地址; ``` ## 2.3.mha配置文件参数 |参数名字|示意|参数作用域|默认值|示例| |----|----|-----|----|----| |hostname|目标实例的主机名或者IP地址|Local Only|-|hostname=mysql_server1, hostname=192.168.0.1, etc| |ip|MHA会自动解析Hostname获得|Local Only|gethostbyname($hostname)|ip=192.168.1.3| |port|目标实例的端口|Local/App/Global|3306|port=3306| |ssh_host|No|Local Only|same as hostname|ssh_host=mysql_server1, ssh_host=192.168.0.1, etc| |ssh_ip|No|Local Only|gethostbyname($ssh_host)|ssh_ip=192.168.1.3| |ssh_port|No|Local/App/Global|22|ssh_port=22| |ssh_connection_timeout|No|Local/App/Global|5|ssh_connection_timeout=20| |ssh_options|No|Local/App/Global|""(empty string)|ssh_options="-i /root/.ssh/id_dsa2"| |candidate_master|提升这个从库被提升为新主库的优先级|Local Only|0|candidate_master=1| |no_master|永远也不会变为新主库|Local Only|0|no_master=1| |ignore_fail|从库有故障的情况下,仍然继续进行故障转移|Local Only|0|ignore_fail=1| |skip_init_ssh_check|No|Local Only|0|skip_init_ssh_check=1| |skip_reset_slave|No|Local/App/Global|0|skip_reset_slave=1| |user|No|Local/App/Global|root|user=mysql_root| |password|No|Local/App/Global|""(empty string)|password=rootpass| |repl_user|No|Local/App/Global|Master_User value from SHOW SLAVE STATUS|repl_user=repl| |repl_password|No|Local/App/Global|(current replication password)|repl_user=replpass| |disable_log_bin|No|Local/App/Global|0|disable_log_bin=1| |master_pid_file|No|Local/App/Global|""(empty string)|master_pid_file=/var/lib/mysql/master1.pid| |ssh_user|No|Local/App/Global|current OS user|ssh_user=root| |remote_workdir|No|Local/App/Global|/var/tmp|remote_workdir=/var/log/masterha/app1| |master_binlog_dir|保存主库binlog的目录,用于数据补偿.|Local/App/Global|/var/lib/mysql|master_binlog_dir=/data/mysql1,/data/mysql2| |log_level|No|App/Global|info|log_level=debug| |manager_workdir|mha manager生成的相关状态文件的绝对路径|App|/var/tmp|manager_workdir=/var/log/masterha| |client_bindir|No|App|-|client_bindir=/usr/mysql/bin| |client_libdir|No|App|-|client_libdir=/usr/lib/mysql| |manager_log|mha manager生成的日志据对路径|App|STDERR|manager_log=/var/log/masterha/app1.log| |check_repl_delay|默认情况下,如果从库落后主库100M的relay logs,MHA不会选择这个从库作为新主库|App/Global|1|check_repl_delay=0| |check_repl_filter|No|App/Global|1|check_repl_filter=0| |latest_priority|调整优先级顺序|App/Global|1|latest_priority=0| |multi_tier_slave|支持多个主库的复制架构|App/Global|0|multi_tier_slave=1| |ping_interval|这个参数表示mha manager多久ping(执行select ping sql语句)一次master,连续三个丢失ping连接,mha master就判定mater死了,因此,通过4次ping间隔的最大时间的机制来发现故障,默认是3,表示间隔是3秒|App/Global|3|ping_interval=5| |ping_type|探活类型,默认select,更敏感可以改为connect|App/Global|SELECT|ping_type=CONNECT| |secondary_check_script|多个网路脚本监测master可用性|App/Global|null|secondary_check_script= masterha_secondary_check -s remote_dc1 -s remote_dc2| |master_ip_failover_script|VIP故障转移功能|App/Global|null|master_ip_failover_script=/usr/local/custom_script/master_ip_failover| |master_ip_online_change_script||App/Global|null|master_ip_online_change_script= /usr/local/custom_script/master_ip_online_change| |shutdown_script|强制关闭master节点来避免master节点重新启动服务.防止脑裂|App/Global|null|shutdown_script= /usr/local/custom_script/master_shutdown| |report_script|发送报告|App/Global|null|report_script= /usr/local/custom_script/report| |init_conf_load_script|No|App/Global|null|report_script= /usr/local/custom_script/init_conf_loader| # 3.MHA扩展功能使用 ## 3.1.故障转移-VIP ```bash 扩展脚本:master_ip_failover 功能: 1.设置master节点VIP 2.failover时候主节点切换同时VIP也切换到新的主节点 目的:方便对业务透明,一个VIP提供业务服务 #使用 vip : 172.21.188.234/24 vip 故障转移脚本 上传脚本文件到/usr/local/bin 解压 cp -a /chj/class/dowland/mha_script/* /usr/local/bin chmod +x /usr/local/bin/* #修改内容 cp -a ./mha_script/* /usr/local/bin chmod +x /usr/local/bin/* vim /usr/local/bin/master_ip_failover my $vip = '172.21.188.234/24'; my $key = '1'; my $if = 'eth0'; my $ssh_start_vip = "/sbin/ifconfig $if:$key $vip"; my $ssh_stop_vip = "/sbin/ifconfig $if:$key down"; my $ssh_Bcast_arp= "/sbin/arping -I $if -c 3 -A $vip"; #修改Manager 配置文件 vim /data/mha/mha_3306.cnf master_ip_failover_script=/usr/local/bin/master_ip_failover #手工在主库添加VIP ifconfig eth0:1 172.21.188.234/24 ifconfig eth0:1 down #重启MHA masterha_stop --conf=/data/mha/mha_3306.cnf nohup masterha_manager --conf=/data/mha/mha_3306.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /data/mha/mysql3306/manager.log 2>&1 & masterha_check_status --conf=/data/mha/mha_3306.cnf #故障转移 down机测试 #故障恢复 - 搭建新的从节点 - 配置从节点到mha配置文件 - 启动mha ``` ## 3.2.故障提醒(实验) ```bash 准备脚本 cp send_report send_report.bak1 my $smtp='smtp.qq.com'; # smtp服务器 my $mail_from='1584452137@qq.com'; # 发件箱 my $mail_user='mha_test'; # 用户名 QQ号 my $mail_pass='gemghsvgkeyzcagh'; # 授权码 my $mail_to=['1584452137@qq.com']; # 收件箱 #my $mail_to=['to1@qq.com','to2@qq.com']; 修改配置文件 vim /data/mha/mha_3306.cnf # 添加一行: report_script=/usr/local/bin/send_report 重启MHA masterha_stop --conf=/data/mha/mha_3306.cnf nohup masterha_manager --conf=/data/mha/mha_3306.cnf -- remove_dead_master_conf --ignore_last_failover < /dev/null> /var/log/mha/app1/manager.log 2>&1 & ``` ## 3.3.日志补偿binlog-server ```bash 功能: 1.如果mysql master ssh不可达,日志补充可用来充当为新slave补充master binlog数据的地方 2.GTID模式,补充master down宕机数据 #步骤 ##创建binlog存放目录 mkdir -p /data/mha/mysql3306/ chown -R mysql.mysql /data/mha/mysql3306/ cd /data/mha/mysql3306/ ##获取二进制点位 mysql -uqianlong -p123456 -h127.0.0.1 -e "show slave status \G"|grep "Master_Log" Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 154 Relay_Master_Log_File: mysql-bin.000001 Exec_Master_Log_Pos: 154 #检查mha状态 masterha_check_status --conf=/data/mha/mha_3306.cnf #拉去binlog目录 mysqlbinlog -R --host=172.21.188.37 --user=mha --password=mha --raw --stop-never mysql-bin.000001 & #配置文件更新 vim /data/mha/mha_3306.cnf [binlog1] no_master=1 hostname=172.21.188.37 master_binlog_dir=/data/mha/mysql3306/ #重启MHA masterha_stop --conf=/data/mha/mha_3306.cnf nohup masterha_manager --conf=/data/mha/mha_3306.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /data/mha/mysql3306/manager.log 2>&1 & masterha_check_status --conf=/data/mha/mha_3306.cnf ``` ## 3.4.MHA防止脑裂 ```bash #知识补充 脑裂(split-brain),指在一个高可用(HA)系统中,当联系着的两个节点断开联系时,本来为一个整体的系统,分裂为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏。 有状态的HA服务,必须要防止脑裂 #HA集群脑裂解决 1.仲裁 当两个节点出现分歧时,由第3方的仲裁者决定听谁的。这个仲裁者,可能是一个锁服务或者其他 2.fencing 直接干掉不确定的节点状态,确保资源被完全释放 #MHA - manager服务是单个心跳对mysql进行心跳检查 - MHA脑裂:manager服务与master出现网络故障 or 其实master并未宕机(假死),导致判断错误,这种情况很容易导致脑裂引起不必要的failover - 解决方案 - 仲裁:采用多心跳投票机制。例如:多条网络线路(以太网、磁盘网络) - secondary_check_script manager配置多个路由进行网络检查 - fencing:关闭检查有问题的解决服务器 - mha中是shutdown_script脚本 自定义即可 secondary_check_script= /usr/local/bin/masterha_secondary_check -s 172.21.188.36 -s 172.21.188.19 --user=root --master_host=172.21.188.37 --master_ip=172.21.188.37 --master_port=3306 //一旦MHA到master的监控之间出现问题,MHA Manager将会判断其它两个slave是否能建立到master_ip 3306端口的连接 shutdown_script="/usr/local/bin/power_manager" //设置故障发生后关闭故障主机脚本 ``` ## 3.5.MHA日常管理与在线切换 - 日常管理 ```bash 1.ssh检查登入是否成功 masterha_check_ssh --conf=/data/mha/mha_3306.cnf 2.查看复制是否建立好 masterha_check_repl --conf=/data/mha/mha_3306.cnf 3.启动&&停止mha masterha_stop --conf=/data/mha/mha_3306.cnf nohup masterha_manager --conf=/data/mha/mha_3306.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null> /data/mha/mysql3306/manager.log 2>&1 & (1)slave节点宕机,manager无法启动 --ignore_fail_on_start,从节点宕机也可以启动 4.mha启动状态监测 masterha_check_status --conf=/data/mha/mha_3306.cnf 5.failover 切换 (1)在failover后,下次重启mha manager 每次failover 切换后会在管理目录生成文件mha_3306.failover.complete作为标识,如果下次切换有这个文件不成功,如何解决 - 删除 rm - 参数 --ignore_last_failover 来启动mha manager (2)参数last_failover_minute=(minutes) 当最近的一个failover 切换发生在last_failover_minute(默认为8小时) 之内或者failover切换失败,MHA manager 将不会在切换。如何解决 - 加上参数 --ignore_last_failover (3)wait_on_failover_error=(seconds) 在failover的过程,当发出错误了,masterha_manager 等待 wait_no_failover_error 的时间后,退出。如果设置为了0,直接退出。 (4)--remove_dead_master_conf 设置此参数,当成功failover后,MHA manager将会自动删除配置文件中关于dead master的配置选项。 ``` - 在线切换failover ```bash 1.场景应用 - 业务迁移,比如版本升级及主动主从切换等。MHA 提供快速切换和优雅的阻塞写入,这个切换过程只需要 0.5-2s 的时间,这段时间内数据是无法写入的,需要跟业务提前告知 - mha自动failover失效,手动failover - 手工切换,需要先停掉运行mha在切换 2.需求功能 - vip自动切换 - 自己额外编写切换逻辑 - mha自带脚本master_ip_online_change 3.需求条件 - 所有从库的 IO线程正常运行。 - 所有从库的 SQL线程正常运行。 - 所有从库上slave参数Seconds_Behind_Master小于或者等于--running_updates_limit的时间 - 在master端,通过show processlist输出,没有一个更新花费的时间大于running_updates_limit秒。 4.切换流程: - 检测复制设置和确定当前主服务器 - 确定新的主服务器 - 阻塞写入到当前主服务器 - 等待所有从服务器赶上复制 - 授予写入到新的主服务器 - 重新设置从服务器 4.场景演练 (1)master down但是masterha_manager没有开启或者失效 masterha_master_switch --master_state=dead --dead_master_host=172.21.188.37 --new_master_host=172.21.188.36 --conf=/data/mha/mha_3306.cnf #注:原master重启后需要重新挂起主从,no gitd需要看输出日志的binlog,gitd不需要 auto即可 (2)master未宕机,手动在线切换 - 停掉mha masterha_stop --conf=/chj/class/mha/mha_3306.cnf - 添加参数 vim /usr/local/bin/master_ip_online_change ########################################################################### my $vip = "172.21.188.234/24"; my $key = "1"; my $ssh_start_vip = "/sbin/ifconfig eth0:$key $vip"; my $ssh_stop_vip = "/sbin/ifconfig eth0:$key $vip down"; my $ssh_Bcast_arp= "/sbin/arping -I eth0 -c 3 -A 172.21.188.234"; ########################################################################### vim /chj/class/mha/mha_3306.cnf master_ip_online_change_script=/usr/local/bin/master_ip_online_change - 在线切换 masterha_master_switch --conf=/chj/class/mha/mha_3306.cnf --master_state=alive --new_master_host=172.21.188.36 --orig_master_is_new_slave --running_updates_limit=10000 #orig_master_is_new_slave 将原master变为slave节点 #running_updates_limit=10000,忽略主从延时,单位s. - binlog——server重构 mysqlbinlog -R --host=172.21.188.36 --user=mha --password=mha --raw --stop-never mysql-bin.000001 & - 启动mha+状态检查 ``` ## 3.6.MHA数据一致性保证参数 ```bash #*************** rpl_semi_sync *************** rpl_semi_sync_master_enabled =ON rpl_semi_sync_master_timeout =5000 rpl_semi_sync_master_wait_for_slave_count =1 rpl_semi_sync_master_wait_no_slave =ON rpl_semi_sync_master_wait_point =AFTER_SYNC rpl_semi_sync_slave_enabled =ON #*************** group commit *************** binlog_group_commit_sync_delay =1 binlog_group_commit_sync_no_delay_count =1000 #*************** gtid *************** gtid_mode =ON enforce_gtid_consistency =ON log_slave_update =1 slave_parallel_type =LOGICAL_CLOCK slave_parallel_workers =4 master_info_repository =TABLE relay_log_info_repository =TABLE ``` # 4.MHA-failover核心原理流程解析(源码级别) ## 4.1.NO-GTID - 流程 ```bash 流程解析(源码级别详细版) 1.Configuration Check Phase - init_config(): 初始化配置 - MHA::ServerManager::init_binlog_server: 初始化binlog server - check_settings() - check_node_version(): 查看MHA的版本 - connect_all_and_read_server_status(): 检测确认各个Node节点MySQL是否可以连接 - get_dead_servers(),get_alive_servers(),get_alive_slaves():再次检测一次node节点的状态 - print_dead_servers(): 是否挂掉的master是否是当前的master - 快速判断dead server,是否真的挂了,不会double check - 检测上次的failover文件,如果8h内,这次就不会failover,除非配置了额外的参数 - start_sql_threads_if(): 查看所有slave的Slave_SQL_Running是否为Yes,若不是则启动SQL thread - is_gtid_auto_pos_enabled(): 判断是否是GTID模式 2.Dead Master Shutdown Phase - stop_io_thread(): stop所有slave的IO_thread - force_shutdown_internal($dead_master): - master_ip_failover_script: 如果有这个脚本,则执行里面的逻辑(比如:切换vip) - shutdown_script:如果有这个脚本,则执行里面的逻辑(比如:Power off 服务器) 3.Master Recovery Phase、 3.1 Getting Latest Slaves Phase:check_set_latest_slaves() - read_slave_status(): 获取所有show slave status 信息 - identify_latest_slaves(): 找到最新的slave是哪个 - identify_oldest_slaves(): 找到最老的slave是哪个 3.2. Saving Dead Master's Binlog Phase:save_master_binlog() - 如果dead master可ssh - save_master_binlog_internal: 用mha node节点save_binary_logs脚本拷贝相应binlog到manager - diff_binary_log:生产差异binlog日志 - file_copy:差异binlog拷贝到manager节点的 manager_workdir目录下 - 如果dead master不可以ssh:差异日志丢失 3.3.Determining New Master Phase. - GTID auto_pos没有打开,调用find_latest_base_slave() - find_latest_base_slave_internal:寻找拥有所有relay-log的最新slave,如果没有,则failover失败、 - apply_diff_relay_logs:查看最新的slave是否有其他slave缺失的relay-log - select_new_master: 选举new master - MHA::ServerManager::select_new_master: - get_candidate_masters(): 获取配置中候选节点 - get_bad_candidate_masters(): 以下条件不能成为候选master - dead server,no_master >= 1,log_bin=0,check_slave_delay延时过高 - 选举流程:根据五次选择顺序选举。 3.4.New Master Diff Log Generation Phase - recover_master_internal - recover_relay_logs:判断new master是否为最新的slave,如果不是,则生产差异relay logs,并发送给新master - recover_master_internal:之前生产的dead master上的binlog传送给new master 3.5.Master Log Apply Phase - apply_diff: - wait_until_relay_log_applied:直到new master完成所有relay log,否则一直等待 - 判断Exec_Master_Log_Pos == Read_Master_Log_Pos,如果不等,那么生产差异日志,save_binary_logs --command=save - apply_diff_relay_logs --command=apply:对new master进行恢复 - exec_diff:Exec_Master_Log_Pos和Read_Master_Log_Pos的差异日志 - read_diff:new master与lastest slave的relay log的差异日志 - binlog_diff:lastest slave与daed master之间的binlog差异日志 - 设置了master_ip_failover_script脚本,那么会执行这里面的脚本(一般用来漂移vip) - disable_read_only(): 允许new master可写 4.Slaves Recovery Phase 4.1.Starting Parallel Slave Diff Log Generation Phase - recover_all_slaves_relay_logs:生成Slave与New Slave之间的差异日志,并将该日志拷贝到各Slave的工作目录下 4.2.Starting Parallel Slave Log Apply Phase.. - recover_slave: - (并发)中继补偿,生成read_to_latest - (并发)将早生成的read_to_tail部分,拷贝到各个从库,应用差异日志,指向新主库,启动复制 5. New master cleanup phase reset_slave_on_new_master:在new master上执行reset slave all; ``` ![Untitled (37)](https://img.sunrisenan.com/img/2024/04/27/150305189.png) - 图示版 ![Untitled (38)](https://img.sunrisenan.com/img/2024/04/27/150356809.png) ## 4.2.GTID - 流程 ```bash 1: 检查配置 - 读取配置文件 - 初始化binlog server a. 查看MHA的版本 b. 检测确认各个Node节点MySQL是否可以连接 c. 再次检测一次node节点的状态 d. 是否挂掉的master是否是当前的master e. 快速判断dead server,是否真的挂了,如果ping_type=insert,不会double check f. 检测上次的failover文件 g. 如果上次failover的时间在8小时以内,那么这次就不会failover,除非配置了额外的参数 h. 查看所有slave的Slave_SQL_Running是否为Yes,若不是则启动SQL thread i: 判断是否是GTID模式 2:主Mater关机阶段 a. stop所有slave的IO_thread,只要有一个在线从库的salve io线程停止失败,那么就终止切换 b. master_ip_failover_script: 如果有这个脚本,则执行里面的逻辑(比如:切换vip) shutdown_script:如果有这个脚本,则执行里面的逻辑,这两部防止脑裂 3: 主库恢复阶段 3.1: 找到最新slave a. read_slave_status(): 获取所有show slave status 信息 b. identify_latest_slaves(): 找到最新的slave是哪个 c. identify_oldest_slaves(): 找到最老的slave是哪个 3.2: Saving Dead Masters Binlog Phase (GTID 模式下没有这一步) 3.3: Determining New Master Phase.. a.获取最新的slave c.选举new master - 获取配置中候选节点 - 以下条件不能成为候选master # dead server # no_master >= 1 # log_bin=0 # oldest_major_version=0 # check_slave_delay: 检查是否延迟非常厉害(可以通过设置no_check_delay忽 - 根据五次选择策略选举 3.4: 新Master数据恢复阶段 1.recover_master_gtid_internal: - 候选master等待所有relay-log都应用完 - 如果候选master不是最新的slave: - 最新的slave应用完所有的relay-log - 让候选master同步到latest slave,追上latest slave。获取候选master此时此刻的日志信息,以便后面切换 - 如果候选master是最新的slave: - 获取候选master此时此刻的日志信息,以便后面切换 - 如果配置binlog server,那么在binlogsever 能连的情况下,将binlog 拷贝到Manager,并生成差异日志diff_binlog(save_binary_logs --command=save) - 应用差异的binlog到new master 4.slave恢复阶段 4.1.GTID模式:因为master已经恢复,那么slave直接change master auto_pos=1 的模式就可以恢复 用此等待同步全部追上 5: New master cleanup phase.. - 在new master上执行reset slave all; ``` ![Untitled (39)](https://img.sunrisenan.com/img/2024/04/27/150534304.png) ![image (12)](https://img.sunrisenan.com/img/2024/04/27/150610708.png) # 5.源码角度分析MHA存在的问题及规避方案 > GTID模式下MHA源码有些地方不完善 ## 5.1.MHA源码逻辑缺陷 - 典型的MHA架构+主从异步复制 ![Untitled (40)](https://img.sunrisenan.com/img/2024/04/27/150722118.png) - 风险点1 ```bash GTID模式下: - master-down(选举slave2为 new master,slave1为lastest slave) - new_master会change到last_slave上面去做数据补偿,而不是去old_master(就是crash_master)上去做数据补偿 - 丢失ID=4这条记录 非GTID情况下: - 有数据补偿 old master,ID4这条可以从主库上面取补偿. - 但是读取s1上面relay_log 如果配置了自动清理relay_log 这样就会导致ID3这条记录都取不到(不要配自动清relaylog) MHA源码: rpm2cpio *.rpm | cpio -div #源码解包 /usr/share/perl5/vendor_perl/MHA/MasterFailover.pm ``` ![Untitled (41)](https://img.sunrisenan.com/img/2024/04/27/150833890.png) - 风险点2 ```bash - gtid情况下,他会等待所有的relay_log 应用完毕.主从延时很大,则数据恢复时间不允许 sub recover_master_gtid_internal($$$) { my $target = shift; my $latest_slave = shift; my $binlog_server_ref = shift; my $relay_master_log_file; my $exec_master_log_pos; $log->info(); $log->info("* Phase 3.3: New Master Recovery Phase../n"); $log->info(); $log->info(" Waiting all logs to be applied.. "); my $ret = $target->wait_until_relay_log_applied($log); #这里是个永久等待 ``` ## 5.2.MHA源码逻辑缺陷规避 - 针对第一个风险点 ```bash - GTID:开启binlog_server作为数据补偿,master down后会从binlog_server拿去master差异的binlog if ($ret) { $log->error(" Failed with return code $ret"); return -1; } $log->info(" done."); $target->stop_slave($log); if ( $target->{id} ne $latest_slave->{id} ) { $log->info( sprintf( " Replicating from the latest slave %s and waiting to apply..", $latest_slave->get_hostinfo() ) ); $log->info(" Waiting all logs to be applied on the latest slave.. "); $ret = $latest_slave->wait_until_relay_log_applied($log); if ($ret) { $log->error(" Failed with return code $ret"); return -1; } $latest_slave->current_slave_position(); $relay_master_log_file = $latest_slave->{Relay_Master_Log_File}; $exec_master_log_pos = $latest_slave->{Exec_Master_Log_Pos}; $ret = $_server_manager->change_master_and_start_slave( $target, $latest_slave, undef, undef, $log ); if ($ret) { $log->error(" Failed with return code $ret"); return -1; } $ret = $_server_manager->wait_until_in_sync( $target, $latest_slave ); if ($ret) { $log->error(" Failed with return code $ret"); return -1; } $log->info(" done."); } else { $target->current_slave_position(); $relay_master_log_file = $target->{Relay_Master_Log_File}; $exec_master_log_pos = $target->{Exec_Master_Log_Pos}; } if ( #这里 如果配置binlog_server 得到file和pos和差异binlog save_from_binlog_server( $relay_master_log_file, $exec_master_log_pos, $binlog_server_ref ) ) { apply_binlog_to_master($target); } return $_server_manager->get_new_master_binlog_position($target); } - 非GTID模式 - 从库不要配置自动清理relay_log,这样就会导致ID3这条记录都取不到(不要配自动清relaylog - MHA自带工具 purge_relay_logs+crontab清理 - 半同步无损复制打开 ``` ## 5.3.使用总结 ```bash 0.关于数据一致性 即使 用了 无损复制+binlogserver 也一定要对核心业务做数据校验 pt-checksum 1 MHA+VIP 缺点 1.1 如果 MHA manger 节点和数据库 master 节点在不同的网络,可能会因为短时间的网络问题触发 failover 而造成 VIP 脑裂现象。 解决办法: 设置 masterha_secondary_check 脚本增加第三方检测。 尽量保证 manger 节点在同一网段,并添加监控。 1.2 如果 MySQL 采用单机多实例部署方式,VIP 的切换将会影响剩下实例的使用。 解决办法:采用非 VIP 切换方式,DNS切换、中间件切换、zk 切换等; 2 GTID 的潜在风险 2.1 由阅读源码可知在 GTID 模式下做 failover 会跳过 save_master_binlog。 解决办法: 配置 binlog server 或者把主库配置成 binlog server,虽然在 save_master_binlog 阶段不做 save binlog,但是在 recover_master 过程中会调用 binlog server 去补齐日志。 升级 MySQL 至 5.7 版本并采用半同步复制模式。 2.2 新主 binlog 缺失问题。 由于 GTID 模式不指定复制位点,直接使用 GTID 编号来同步,所以实际在使用中,切换之后新的 GTID 必须是连续才能设置成功。这就可能会出现新主库在很久以前执行过类似更改用户权限这类产生 binlog 事件的命令,随着时间的推移,这些日志可能已经被清理掉了。这样被提升为新主后就会出现从库找不到 binlog 的情况。 解决办法:从库开启只读模式、从库用户只提供 select 权限、设置 binlog 事件监控等。 3 Failover 相关问题 3.1 MHA 在完成 failover 后会退出。 解决办法:部署 daemontools 工具,使 manager 以守护进程的方式运行。 3.2 两次 failover 时间小于 8 小时导致 failover 失败问题。 解决办法:启动时增加 --ignore_last_failover 参数。 3.3 因某一 slave 故障导致 failover 失败问题。 解决办法:配置文件中添加 ignore_fail 参数。 4 其他问题 4.1 Relay log 可能会被误清理的问题。 MySQL 主从复制在缺省情况下,从库的 relay log 会在 SQL线程执行完毕后被自动删除,但是在 MHA 场景下,某些滞后从库的恢复依赖于其他从库的relay log。 解决办法: 设置 relay_log_purge 参数为 0。 使用 purge_relay_logs 脚本定时清理 relay log。 Tip:purge_relay_logs 在清理 relay log 过程中会采用 ln -l 方式归档最近一个 relay log,不会出现 recover slave 过程中找不到 relay log 的情况。 4.2 ping_type 选择。 MHA 默认是建立一个长连接,然后通过 select 1 (ping_type=SELECT) 检查连通性。但是在某些情况下,最好是通过连接/断开的方式检查,因为它可以更严格的检查 tcp 可连接性,设置 ping_type=connect 开启。 4.3 MHA failover 过程中 new master 和 slaves 的恢复顺序是咋样的? 先对 new master 做日志补偿,然后对 slaves 做日志补偿 4.5 MHA 是怎样获取 latest slave 的? 通过 show slave status 命令获取 Master_Log_File 、Read_Master_Log_Pos 值比较来获取 latest slave . ```
李延召
2024年4月27日 15:12
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码