共计 6880 个字符,预计需要花费 18 分钟才能阅读完成。
本篇文章给大家分享的是有关如何使用 MySQL MHA 源代码进行监控检查,丸趣 TV 小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着丸趣 TV 小编一起来看看吧。
一、前言
在研究的同时,把 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 every interval 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
# interval 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 );
三种检测机制函数
#这个 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 源代码进行监控检查,丸趣 TV 小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注丸趣 TV 行业资讯频道。