一、MHA在Binlog模式下切换过程解析
Phase 1: Configuration Check Phase
- init_config():初始化配置
- MHA::ServerManager::init_binlog_server:初始化binlog server
- check_settings():检查相关配置
1 2 3 4 5 6 7 8 |
a. check_node_version() #查看MHA的版本; b. connect_all_and_read_server_status() #检测确认各个Node节点MySQL是否可以连接; c. get_dead_servers(),get_alive_servers(),get_alive_slaves() #再次检测一次node节点的状态; d. print_dead_servers() #挂掉的master是否是当前的master; e. MHA::DBHelper::check_connection_fast_util #快速判断dead server,是否真的挂了,如果ping_type=insert,不会double check; f. MHA::NodeUtil::drop_file_if($_failover_error_file|$_failover_complete_file) #检测上次的failover文件; g. .... #如果上次failover的时间在8小时以内,那么这次就不会failover,除非配置了额外的参数; h. start_sql_threads_if() #查看所有slave的Slave_SQL_Running是否为Yes,若不是则启动SQL thread; |
- is_gtid_auto_pos_enabled():判断是否是GTID模式
Phase 2: Dead Master Shutdown Phase..
- force_shutdown($dead_master)
1 2 3 4 |
a. stop_io_thread() #停止所有slave的IO_thread; b. force_shutdown_internal($dead_master) b_1. master_ip_failover_script #如果有这个脚本,则执行里面的逻辑(比如:切换vip); b_2. shutdown_script #如果有这个脚本,则执行里面的逻辑(比如:强制Master shutdown); |
Phase 3: Master Recovery Phase..
- Phase 3.1: Getting Latest Slaves Phase..
1 2 3 4 |
* check_set_latest_slaves() a. read_slave_status() #获取所有show slave status信息; b. identify_latest_slaves() #找到延迟最小的slave是哪个,并且会记录复制到主二进制日志的文件和位置; c. identify_oldest_slaves() #找到延迟最大的slave是哪个; |
- Phase 3.2: Saving Dead Master’s Binlog Phase..
1 2 3 4 5 6 |
* save_master_binlog($dead_master); -> 如果dead master可以ssh,那么; b_1_1. save_master_binlog_internal #用node节点save_binary_logs脚本拷贝相应binlog到manager; diff_binary_log #根据Phase3.1获取的最新slave信息,在Master上利用mysqlbinlog生产差异binlog日志; b_1_2. file_copy #将差异binlog拷贝到manager节点的manager_workdir目录下; -> 如果dead master不可以ssh,那么差异日志就会丢失; |
- Phase 3.3: Determining New Master Phase..
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
b. 如果GTID auto_pos没有打开,调用find_latest_base_slave() b_1. find_latest_base_slave_internal: 寻找拥有所有relay-log的最新slave,如果没有,则failover失败; b_1_1. find_slave_with_all_relay_logs: b_1_1_1. apply_diff_relay_logs: 查看最新的slave是否有其他slave缺失的relay-log; c. select_new_master: 选举new master c_1. MHA::ServerManager::select_new_master get_candidate_masters(): 获取配置中候选节点; get_bad_candidate_masters(): 以下条件不能成为候选master; # dead server # no_master >= 1 # log_bin=0 # oldest_major_version=0 # check_slave_delay: 检查是否延迟非常厉害(可以通过设置no_check_delay忽略) {Exec_Master_Log_Pos} + 100000000只要binlog position不超过100000000就行; # 选举流程:先看candidate_master,然后找latest slave, 然后再随机挑选; |
- Phase 3.3: New Master Diff Log Generation Phase..
1 2 3 |
* recover_master_internal recover_relay_logs #判断new master是否为最新的slave,如果不是,则跟laste slave生产差异relay logs,然后manager使用scp发送给new master; recover_master_internal #manager将之前生产的dead master上的binlog传送给new master; |
- Phase 3.4: Master Log Apply Phase..
1 2 3 4 5 6 7 8 9 10 11 |
* apply_diff a. wait_until_relay_log_applied: 直到new master完成所有relay log,否则一直等待; b. 判断Exec_Master_Log_Pos == Read_Master_Log_Pos,如果不等,那么生产差异binlog日志; save_binary_logs --command=save c. apply_diff_relay_logs --command=apply:对new master进行恢复。 c_1. exec_diff:Exec_Master_Log_Pos和Read_Master_Log_Pos的差异日志。 c_2. read_diff:new master与lastest slave的relay log的差异日志。 c_3. binlog_diff:lastest slave与daed master之间的binlog差异日志。 * 获取new master binlog的文件和位置; * 如果设置了master_ip_failover_script脚本,那么会执行这里面的脚本(一般用来漂移vip); * disable_read_only(): 允许new master可写; |
Phase 4: Slaves Recovery Phase..
- Phase 4.1: Starting Parallel Slave Diff Log Generation Phase..
1 |
* recover_all_slaves_relay_logs #生成Slave与New Slave之间的差异日志,并将该日志拷贝到各Slave的工作目录下; |
- Phase 4.2: Starting Parallel Slave Log Apply Phase..
1 2 |
* recover_slave #对每个slave进行恢复,跟以上Phase 3.4: Master Log Apply Phase中的apply_diff一样; * change_master_and_start_slave #各个slave执行reset slave并重新change master到new master,并且start slave; |
Phase 5: New master cleanup phase..
1 |
reset_slave_on_new_master #在new master上执行reset slave all; |
整个过程核心切换逻辑简化后如下描述:
Phase 1:配置文件检查
Phase 2:非存活Master关闭服务
Phase 3:Master恢复
Phase 3.1:获取与Master延迟最小的Slave节点和延迟最大的Slave节点
Phase 3.2:生成Master与延迟最小的Slave节点的差异binlog并保存到manager节点
Phase 3.3:找出新的New Master,并且在延迟最小的Slave的Relay log中寻找延迟最小Slave与延迟最大Slave之间差异的binlog日志是否存在
Phase 3.3:如果New Master不是最新的Slave节点,那么需要从最新Slave的Relay log中生成它们之间的差异Relay log
Phase 3.4:New Master恢复差异Relay log和差异binlog日志,随后获取Master binlog位点信息
Phase 4:Slaves恢复
Phase 4.1:多线程生成延迟最小的Slave节点与其他一个或多个Slave差异Relay log
Phase 4.2:多线程恢复Slave节点与延迟最小的Slave之间的差异Relay log,并且恢复manager节点保存的差异binlog,然后change master到NEW MASTER节点
Phase 5:New Master清理Slave信息,并删除掉MHA配置文件中的选主信息防止误操作等
二、MHA在GTID模式下切换过程解析
Phase 1: Configuration Check Phase
- init_config(): 初始化配置。
- MHA::ServerManager::init_binlog_server: 初始化binlog server。
- check_settings()
1 2 3 4 5 6 7 8 |
a. check_node_version() #查看MHA的版本; b. connect_all_and_read_server_status() #检测确认各个Node节点MySQL是否可以连接; c. get_dead_servers(),get_alive_servers(),get_alive_slaves() #再次检测一次node节点的状态; d. print_dead_servers() #是否挂掉的master是否是当前的master; e. MHA::DBHelper::check_connection_fast_util #快速判断dead server,是否真的挂了,如果ping_type=insert,不会double check; f. MHA::NodeUtil::drop_file_if($_failover_error_file|$_failover_complete_file) #检测上次的failover文件; g. ... #如果上次failover的时间在8小时以内,那么这次就不会failover,除非配置了额外的参数; h. start_sql_threads_if() #查看所有slave的Slave_SQL_Running是否为Yes,若不是则启动SQL thread; |
- is_gtid_auto_pos_enabled():判断是否是GTID模式。
Phase 2: Dead Master Shutdown Phase completed.
- force_shutdown($dead_master):
1 2 3 4 |
a. stop_io_thread() #stop所有slave的IO_thread; b. force_shutdown_internal($dead_master): b_1. master_ip_failover_script #如果有这个脚本,则执行里面的逻辑(比如:切换vip); b_2. shutdown_script #如果有这个脚本,则执行里面的逻辑(比如:Power off服务器); |
Phase 3: Master Recovery Phase..
- Phase 3.1: Getting Latest Slaves Phase..
1 2 3 4 |
* check_set_latest_slaves() a. read_slave_status() #获取所有show slave status信息; b. identify_latest_slaves() #找到延迟最小的slave是哪个; c. identify_oldest_slaves() #找到延迟最大的slave是哪个; |
- Phase 3.2: Saving Dead Master’s Binlog Phase.. (GTID 模式下没有这一步)
- Phase 3.3: Determining New Master Phase..
1 2 3 4 5 6 7 8 9 10 11 |
get_most_advanced_latest_slave() #获取最新的slave; c. select_new_master #选举new master; c_1. MHA::ServerManager::select_new_master: get_candidate_masters() #获取配置中候选节点; get_bad_candidate_masters() #以下条件不能成为候选master; # dead server # no_master >= 1 # log_bin=0 # oldest_major_version=0 # check_slave_delay #检查是否延迟非常厉害(可以通过设置no_check_delay忽略);{Exec_Master_Log_Pos} + 100000000只要binlog position不超过100000000就行; # 选举流程:先看candidate_master,然后找latest slave,然后再随机挑选; |
- Phase 3.3: New Master Recovery Phase..
1 2 3 4 5 6 7 8 9 10 |
* recover_master_gtid_internal: wait_until_relay_log_applied #候选master等待所有relay-log都应用完; # 如果候选master不是最新的slave: $latest_slave->wait_until_relay_log_applied($log) #最新的slave应用完所有的relay-log; change_master_and_start_slave #让候选master同步到latest master,追上latest slave; #获取候选master此时此刻的日志信息,以便后面切换; # 如果候选master是最新的slave: # 获取候选master此时此刻的日志信息,以便后面切换; save_from_binlog_server: #如果配置了binlog server,那么在binlogsever能连的情况下,将binlog拷贝到Manager,并生成差异日志diff_binlog(save_binary_logs --command=save); apply_binlog_to_master(Applying differential binlog): #应用差异的binlog到new master; |
Phase 4: Slaves Recovery Phase..
- Phase 4.1: Starting Slaves in parallel..
1 2 3 |
* recover_slaves_gtid_internal: change_master_and_start_slave #因为master已经恢复,那么slave直接change master auto_pos=1的模式就可以恢复; gtid_wait #用此等待同步全部追上; |
Phase 5: New master cleanup phase..
1 |
* reset_slave_on_new_master #在new master上执行reset slave all; |
转载(有修改)
http://keithlan.github.io/2016/08/18/mha_source
https://github.com/yoshinorim/mha4mysql-manager/blob/master/lib/MHA/MasterFailover.pm