【MySQL】MHA原始碼之監控檢查(一)
一、前言
弄完了MHA+Consul架構(包括程式要寫的示例程式碼),在研究的同時,把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)注意:這裡只列出了核心函式,其實在程式啟動的時候,還有一些啟動情況檢查,基本是主庫是否可連線,配置是否正確,從庫是否正常等等
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30221425/viewspace-2674628/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL MHA 執行狀態監控MySql
- 【MySQL】MHA原始碼之主庫選取(二)MySql原始碼
- 分享實用監控指令碼:使用Shell檢查程式是否存在指令碼
- MySQL 5.6大查詢和大事務監控指令碼(Python 2)MySql指令碼Python
- 騰訊效能監控框架Matrix原始碼分析之第一篇框架原始碼
- PostgreSQL之鎖監控指令碼SQL指令碼
- MySQL調優效能監控之show profileMySql
- MySQL調優效能監控之performance schemaMySqlORM
- MHA原始碼分析——環境部署原始碼
- MySQL監控工具MySql
- 淺析badjs原始碼(前端監控方案)JS原始碼前端
- Mysql MHA部署-04MHA配置MySql
- MHA選擇主庫原始碼解析原始碼
- Java程式碼中,如何監控Mysql的binlog?JavaMySql
- shell指令碼:監控MySQL服務是否正常指令碼MySql
- Shell指令碼監控MySQL主從狀態指令碼MySql
- MySQL 實現高可用架構之 MHAMySql架構
- Golang原始碼學習:監控執行緒Golang原始碼執行緒
- HackingTeam原始碼洩漏——語音監控分析原始碼
- MySQL sys效能監控MySql
- [原始碼解析] PyTorch 分散式之彈性訓練(6)---監控/容錯原始碼PyTorch分散式
- MySQL監控-Datadog資料庫監控調研MySql資料庫
- MySQL調優使用者監控之show processlistMySql
- MySQL MHA部署 Part 5 MHA部署指南MySql
- TenSunS監控Mysql:如何優雅的使用一個mysqld_exporter監控所有的MySQL例項MySqlExport
- 11.prometheus監控之黑盒(blackbox)監控Prometheus
- Flutter異常監控 - 肆 | Rollbar原始碼賞析Flutter原始碼
- 系統監控&JVM監控指標資料查詢JVM指標
- 【MYSQL】Mysql常用檢查sqlMySql
- prometheus之docker監控與告警系列(一)PrometheusDocker
- 【原始碼解讀(一)】EFCORE原始碼解讀之建立DBContext查詢攔截原始碼Context
- Mysql事件監控日誌MySql事件
- mysql效能監控相關MySql
- 搭建Lepus 天兔 監控MySQLMySql
- Prometheus MySQL監控+grafana展示PrometheusMySqlGrafana
- Redis Manager 如何檢視監控Redis
- Kubernetes監控之InfluxDBUX
- Flume監控之Ganglia