mha检测mysql状况方式_【MySQL】MHA源代码之监控检查(一)

一、前言

弄完了MHA+Consul架构(包括程序要写的示例代码),在研究的同时,把MHA的源代码也翻阅了一遍,现在准备把MHA一些重要内容梳理一下,既然是高可用工具,那么健康检测是一个基础工作,只有正确检测了数据库的故障,才能进行数据库的切换;而MHA的布局亦如此:

010604fe4ad02d023aadb8ef32484bb5.png

二、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 every  seconds. 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)注意:这里只列出了核心函数,其实在程序启动的时候,还有一些启动情况检查,基本是主库是否可连接,配置是否正确,从库是否正常等等

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值