您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關如何使用MySQL MHA源代碼進行監控檢查,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
一、前言
在研究的同時,把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 ); } 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源代碼進行監控檢查,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。