扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
本篇文章给大家分享的是有关如何使用MySQL MHA源代码进行监控检查,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
创新互联技术团队10多年来致力于为客户提供成都网站制作、网站建设、成都品牌网站建设、营销型网站、搜索引擎SEO优化等服务。经过多年发展,公司拥有经验丰富的技术团队,先后服务、推广了上1000家网站,包括各类中小企业、企事单位、高校等机构单位。
一、前言
在研究的同时,把MHA的源代码也翻阅了一遍,现在准备把MHA一些重要内容梳理一下,既然是高可用工具,那么健康检测是一个基础工作,只有正确检测了数据库的故障,才能进行数据库的切换;而MHA的布局亦如此:
二、MHA健康检查核心调用函数链
注意我这里的函数调用链的规则是文件名|方法名,方法名中的或者表示的是,通过读取配置文件,执行其中的一个函数
MasterMonitor.pm|MHA::MasterMonitor::main()
->MasterMonitor.pm|MHA::MasterMonitor::wait_until_master_is_dead()
->MasterMonitor.pm|MHA::MasterMonitor::wait_until_master_is_unreachable()
->HealthCheck.pm|MHA::HealthCheck::wait_until_unreachable()
->HealthCheck.pm|MHA::HealthCheck::ping_select(或者)
->HealthCheck.pm|MHA::HealthCheck::ping_insert(或者)
->HealthCheck.pm|MHA::HealthCheck::ping_connect(或者)
三、代码分析
我们主要看HealthCheck.pm|MHA::HealthCheck::wait_until_unreachable的实现
1) 该函数通过一个死循环,检测4次,每次sleep ping_interval秒(这个值在配置文件指定,参数是ping_interval),持续四次失败,就认为数据已经宕机
2)如果有二路检测脚本,需要二路检测脚本检测主库宕机,才是真正的宕机,否则只是推出死循环,结束检测,不切换
3)这里的GETLOCK(姑且说是分布式锁)就是用来保护数据库的访问,防止脚本多次启动的
4)该函数调用了三种经检测方法,如下:
PING_TYPE_CONNECT(ping_select),PING_TYPE_INSERT(ping_insert),PING_TYPE_SELECT(ping select),但是哪种最好呢,我建议是PING_TYPE_CONNECT,实际上PING_TYPE_CONNECT调用了ping_select的方法,比PING_TYPE_CONNECT更具有可靠性
# main function # 返回1,表示数据库有问题,但是不会切换;0表示数据库有问题,会切换(这里同时还会返回ssh连接状态,方便确认是网络问题,还是数据库问题) sub wait_until_unreachable($) { my $self = shift; my $log = $self->{logger}; my $ssh_reachable = 2; my $error_count = 0; my $master_is_down = 0; eval { while (1) { $self->{_tstart} = [gettimeofday]; if ( $self->{_need_reconnect} ) { #测试连接,连接正确返回0,否则返回1 ##这里有分布式GetLOCK,如果有别的会话,获取了分布式锁失败,也算连接不成功 my ( $rc, $mysql_err ) = $self->connect( undef, undef, undef, undef, undef, $error_count ); if ($rc) { #排除权限错误 if ($mysql_err) { if ( #在这里并不是不能访问,可能只是权限错误 grep ( $_ == $mysql_err, @MHA::ManagerConst::ALIVE_ERROR_CODES ) > 0 ) { $log->info( "Got MySQL error $mysql_err, but this is not a MySQL crash. Continue health check.." ); #sleep一段时间 $self->sleep_until(); #好吧,如果是权限错误的话,就一直在这里循环了,那么检测一致认为mysql正常,打印权限日志就行 next; } } $error_count++; $log->warning("Connection failed $error_count time(s).."); #处理失败,更新status_file为20:PING_FAILING $self->handle_failing(); #超过四次就跳出这个循环了 if ( $error_count >= 4 ) { #返回1表示ssh可以可以到达,0表示ssh不能到达 $ssh_reachable = $self->is_ssh_reachable(); #返回为1表示数据库主库已经down,0则没有down $master_is_down = 1 if ( $self->is_secondary_down() ); #退出循环,last last if ($master_is_down); $error_count = 0; } $self->sleep_until(); next; } # connection ok $self->{_need_reconnect} = 0; $log->info( "Ping($self->{ping_type}) succeeded, waiting until MySQL doesn't respond.." ); } #释放连接,如果只是类型为PING_TYPE_CONNECT $self->disconnect_if() if ( $self->{ping_type} eq $MHA::ManagerConst::PING_TYPE_CONNECT ); # Parent process forks one child process. The child process queries # from MySQL everyseconds. The child process may hang on # executing queries. # DBD::mysql 4.022 or earlier does not have an option to set # read timeout, executing queries might take forever. To avoid this, # the parent process kills the child process if it won't exit within # seconds. my $child_exit_code; eval { if ( $self->{ping_type} eq $MHA::ManagerConst::PING_TYPE_CONNECT ) { $child_exit_code = $self->fork_exec( sub { $self->ping_connect() }, "MySQL Ping($self->{ping_type})" ); } elsif ( $self->{ping_type} eq $MHA::ManagerConst::PING_TYPE_SELECT ) { $child_exit_code = $self->fork_exec( sub { $self->ping_select() }, "MySQL Ping($self->{ping_type})" ); } elsif ( $self->{ping_type} eq $MHA::ManagerConst::PING_TYPE_INSERT ) { $child_exit_code = $self->fork_exec( sub { $self->ping_insert() }, "MySQL Ping($self->{ping_type})" ); } else { die "Not supported ping_type!\n"; } }; if ($@) { my $msg = "Unexpected error heppened when pinging! $@"; $log->error($msg); undef $@; $child_exit_code = 1; } if ( $child_exit_code == 0 ) { #ping ok #ping是成功的话,则更新状态,然后将$error_count=0(持续累积4次,那就是连接有问题) $self->update_status_ok(); if ( $error_count > 0 ) { $error_count = 0; } #handle_failing启用了二路检测以及ssh_check这时候没结束需要kill掉 $self->kill_sec_check(); $self->kill_ssh_check(); } #存在其他分布式监控 elsif ( $child_exit_code == 2 ) { $self->{_already_monitored} = 1; croak; } else { # failed on fork_exec $error_count++; $self->{_need_reconnect} = 1; $self->handle_failing(); } $self->sleep_until(); } $log->warning("Master is not reachable from health checker!"); }; if ($@) { my $msg = "Got error when monitoring master: $@"; $log->warning($msg); undef $@; return 2 if ( $self->{_already_monitored} ); return 1; } #$master_is_down=0,返回1 return 1 unless ($master_is_down); #0,$ssh_reachable返回1表示ssh可以可以到达,0表示ssh不能到达 return ( 0, $ssh_reachable ); } 1; 三种检测机制函数 #这个ping_connect正常返回0,错误返回1或者2,1是连接存在问题,2是获取锁失败 #改函数调用了ping_select sub ping_connect($) { my $self = shift; my $log = $self->{logger}; my $dbh; my $rc = 1; my $max_retries = 2; eval { my $ping_start = [gettimeofday]; #连接max_retries次,如果有错误,则退出 while ( !$self->{dbh} && $max_retries-- ) { eval { $rc = $self->connect( 1, $self->{interval}, 0, 0, 1 ); }; if ( !$self->{dbh} && $@ ) { die $@ if ( !$max_retries ); } } #ping_select()正常返回为0,错误返回为1 $rc = $self->ping_select(); # To hold advisory lock for some periods of time #获取锁可能需要一定时间,所以在释放连接之前,需要等待一点时间 $self->sleep_until( $ping_start, $self->{interval} - 1.5 ); $self->disconnect_if(); }; if ($@) { my $msg = "Got error on MySQL connect ping: $@"; undef $@; $msg .= $DBI::err if ($DBI::err); $msg .= " ($DBI::errstr)" if ($DBI::errstr); $log->warning($msg) if ($log); $rc = 1; } return 2 if ( $self->{_already_monitored} ); return $rc; } #语句SELECT 1 As Value,正常返回0,错误返回为1 sub ping_select($) { my $self = shift; my $log = $self->{logger}; my $dbh = $self->{dbh}; my ( $query, $sth, $href ); eval { $dbh->{RaiseError} = 1; $sth = $dbh->prepare("SELECT 1 As Value"); $sth->execute(); $href = $sth->fetchrow_hashref; if ( !defined($href) || !defined( $href->{Value} ) || $href->{Value} != 1 ) { die; } }; if ($@) { my $msg = "Got error on MySQL select ping: "; undef $@; $msg .= $DBI::err if ($DBI::err); $msg .= " ($DBI::errstr)" if ($DBI::errstr); $log->warning($msg) if ($log); return 1; } return 0; } #正常返回0,错误返回1,有个疑问,这里见得数据库表貌似没有清理吧 sub ping_insert($) { my $self = shift; my $log = $self->{logger}; my $dbh = $self->{dbh}; my ( $query, $sth, $href ); eval { $dbh->{RaiseError} = 1; $dbh->do("CREATE DATABASE IF NOT EXISTS infra"); $dbh->do( "CREATE TABLE IF NOT EXISTS infra.chk_masterha (`key` tinyint NOT NULL primary key,`val` int(10) unsigned NOT NULL DEFAULT '0') engine=MyISAM" ); $dbh->do( "INSERT INTO infra.chk_masterha values (1,unix_timestamp()) ON DUPLICATE KEY UPDATE val=unix_timestamp()" ); }; if ($@) { my $msg = "Got error on MySQL insert ping: "; undef $@; $msg .= $DBI::err if ($DBI::err); $msg .= " ($DBI::errstr)" if ($DBI::errstr); $log->warning($msg) if ($log); return 1; } return 0; }
四、总结
1)数据库MHA的健康检查,最终调用的ping_select,ping_insert,ping_connect的一种,检测的时间由ping_interval控制,其中ping_connect调用了ping_select
2)MHA最好配置二路检测,否则只是MHA主节点从自身ssh去检测主库是否正常,在MHA管理节点与主库网络存在问题的时候,有可能会发生误切换
3)注意:这里只列出了核心函数,其实在程序启动的时候,还有一些启动情况检查,基本是主库是否可连接,配置是否正确,从库是否正常等等
以上就是如何使用MySQL MHA源代码进行监控检查,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联行业资讯频道。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流