但凡是MySQL DBA肯定都听说过MHA个高可用方案,而且很多公司都是通过对MHA做二次开发来实现MySQL高可用的。如果MHA不结合VIP的话,每次主库切换都需要程序修改连数据库的配置,这样比较麻烦。而采用MHA+VIP的方式时可以在主库切换的过程中让VIP漂移到新主库,省去了改数据库配置这一过程。

公司以前是每一组主从复制集群都配置一个manager结点,然后将vip和网络接口等信息都写死在master_ip_failover和master_ip_online_change脚本中。当主从集群数量太多的情况下要维护的manager结点很多,管理起来很麻烦。

如何实现用一个Manager结点管理多个支持VIP的mysql主从集群呢?有两种实现方式:

1,每一组主从复制集群维护两个切换脚本,将VIP和网络接口信息写死在脚本里。

2,修改MHA的相关模块,使其能识别我们自定义的参数,我们只需要在每一组主从复制集群的配置文件中给参数传值。

很明显,第1种方式太low了,需要维护大量的切换脚本。那我们需要修改哪些模块呢?

看一下masterha_check_repl这段脚本,可以看到检测主从复制状态的时候会调用MasterMonitor模块。

$exit_code=MHA::MasterMonitor::main("--interactive=0","--check_only","--check_repl_health",@ARGV);if($exit_code==0){print"\nMySQLReplicationHealthisOK.\n";}else{print"\nMySQLReplicationHealthisNOTOK!\n";}


通过masterha_master_switch这段脚本可以看到在线切换是调用的MasterRotate模块,故障切换是调用的MasterFailover模块。

if($master_stateeq"dead"){$exit_code=MHA::MasterFailover::main(@ARGV);}elsif($master_stateeq"alive"){$exit_code=MHA::MasterRotate::main(@ARGV);}else{pod2usage(1);}


MasterMonitor.pm,MasterRotate.pm,MasterFailover.pm这三个模块都是调用Config.pm模块来读取参数配置,所以我们只需要修改这几个模块即可。


为了不平的网络环境,我在配置文件加了这三个配置项:

app_vip :主库的VIP

netmask : VIP的网络位

interface :VIP要绑定的网上


对应调整的代码如下:

Config.pm:

申明变量:

my@PARAM_ARRAY=qw/hostnameipportssh_hostssh_ipssh_portssh_connection_timeoutssh_optionsnode_labelcandidate_masterno_masterignore_failskip_init_ssh_checkskip_reset_slaveuserpasswordrepl_userrepl_passworddisable_log_binmaster_pid_filehandle_raw_binlogssh_userremote_workdirmaster_binlog_dirlog_levelmanager_workdirmanager_logcheck_repl_delaycheck_repl_filterlatest_prioritymulti_tier_slaveping_intervalping_typesecondary_check_scriptmaster_ip_failover_scriptmaster_ip_online_change_scriptshutdown_scriptreport_scriptinit_conf_load_scriptclient_bindirclient_libdiruse_gtid_auto_posapp_vipnetmaskinterface/;


给变量赋值:

$value{app_vip}=$param_arg->{app_vip};if(!defined($value{app_vip})){$value{app_vip}=$default->{app_vip};}$value{netmask}=$param_arg->{netmask};if(!defined($value{netmask})){$value{netmask}=$default->{netmask};}$value{interface}=$param_arg->{interface};if(!defined($value{interface})){$value{interface}=$default->{interface};}


MasterMonitor.pm :

修改复制检测时的命令:

"$current_master->{master_ip_failover_script}--command=status--ssh_user=$current_master->{ssh_user}--orig_master_host=$current_master->{hostname}--orig_master_ip=$current_master->{ip}--orig_master_port=$current_master->{port}--app_vip=$current_master->{app_vip}--netmask=$current_master->{netmask}--interface=$current_master->{interface}";


MasterMonitor.pm :

修改停原主库写入的命令:

"$orig_master->{master_ip_online_change_script}--command=stop--orig_master_host=$orig_master->{hostname}--orig_master_ip=$orig_master->{ip}--orig_master_port=$orig_master->{port}--orig_master_user=$orig_master->{escaped_user}--orig_master_password=$orig_master->{escaped_password}--new_master_host=$new_master->{hostname}--new_master_ip=$new_master->{ip}--new_master_port=$new_master->{port}--new_master_user=$new_master->{escaped_user}--new_master_password=$new_master->{escaped_password}--app_vip=$orig_master->{app_vip}--netmask=$orig_master->{netmask}--interface=$orig_master->{interface}";


修改允许在新主库写入的命令:

"$new_master->{master_ip_online_change_script}--command=start--orig_master_host=$orig_master->{hostname}--orig_master_ip=$orig_master->{ip}--orig_master_port=$orig_master->{port}--orig_master_user=$orig_master->{escaped_user}--orig_master_password=$orig_master->{escaped_password}--new_master_host=$new_master->{hostname}--new_master_ip=$new_master->{ip}--new_master_port=$new_master->{port}--new_master_user=$new_master->{escaped_user}--new_master_password=$new_master->{escaped_password}--app_vip=$orig_master->{app_vip}--netmask=$orig_master->{netmask}--interface=$orig_master->{interface}";


MasterFailover.pm:

修改禁用原从库的vip命令:

"$dead_master->{master_ip_failover_script}--orig_master_host=$dead_master->{hostname}--orig_master_ip=$dead_master->{ip}--orig_master_port=$dead_master->{port}--app_vip=$dead_master->{app_vip}--netmask=$dead_master->{netmask}--interface=$dead_master->{interface}";


修改启用原从库vip的命令:

"$new_master->{master_ip_failover_script}--command=start--ssh_user=$new_master->{ssh_user}--orig_master_host=$dead_master->{hostname}--orig_master_ip=$dead_master->{ip}--orig_master_port=$dead_master->{port}--new_master_host=$new_master->{hostname}--new_master_ip=$new_master->{ip}--new_master_port=$new_master->{port}--new_master_user=$new_master->{escaped_user}--new_master_password=$new_master->{escaped_password}--app_vip=$dead_master->{app_vip}--netmask=$dead_master->{netmask}--interface=$dead_master->{interface}";