MHA選擇主庫原始碼解析

技術小能手發表於2018-04-12

MHA在選擇新的主庫之前,會先把活著的slave分為幾個陣列,分別為latest(最靠前的slave陣列),pref(優先被選擇為master的陣列),bad(不會被選擇成為master的slave),slaves(所有活著的slave陣列)。然後進行5次選擇,從上面的這些組當中挑選出新的master。

選擇latest陣列

foreach (@slaves) {
my $a = $latest[0]{Master_Log_File};
my $b = $latest[0]{Read_Master_Log_Pos};
if (
!$find_oldest
&& (
( !$a && !defined($b) )
|| ( $_->{Master_Log_File} gt $latest[0]{Master_Log_File} )
|| ( ( $_->{Master_Log_File} ge $latest[0]{Master_Log_File} )
&& $_->{Read_Master_Log_Pos} > $latest[0]{Read_Master_Log_Pos} )
)
)
{
@latest = ();
push( @latest, $_ );
}
elsif (
$find_oldest
&& (
( !$a && !defined($b) )
|| ( $_->{Master_Log_File} lt $latest[0]{Master_Log_File} )
|| ( ( $_->{Master_Log_File} le $latest[0]{Master_Log_File} )
&& $_->{Read_Master_Log_Pos} < $latest[0]{Read_Master_Log_Pos} )
)
)
{
@latest = ();
push( @latest, $_ );
}
elsif ( ( $_->{Master_Log_File} eq $latest[0]{Master_Log_File} )
&& ( $_->{Read_Master_Log_Pos} == $latest[0]{Read_Master_Log_Pos} ) )
{
push( @latest, $_ );
}
}

上面程式碼主要的結構就是一個foreach迴圈,一個if判斷。foreach迴圈處理所有的活著的slave。if判斷這裡有三個判斷條件,主要根據Master_Log_File和Read_Master_Log_Pos的大小來判斷。第一個和第二個分別為了找出最靠前和最靠後的slave的。如果滿足條件,那麼就清空latest陣列,把符合條件的放入latest陣列裡面。第三個條件用於找出和latest陣列裡面Master_Log_File和Read_Master_Log_Pos一樣的slave,並放入latest陣列。這樣所有的 最靠前的就都放入latest陣列裡面了。

選擇pref陣列

foreach (@servers) {
 next if ( $_->{dead} eq `1` );
 if ( $_->{candidate_master} >= 1 ) {
 push( @ret_servers, $_ );
 }
 }

迴圈處理所有的配置server,已經死了的slave跳過,有引數candidate_master=1的slave放入pref陣列,會被優先推舉為新的master。

選擇bad陣列

 foreach (@servers) {
 if (
 $_->{no_master} >= 1
 || $_->{log_bin} eq `0`
 || $_->{oldest_major_version} eq `0`
 || (
 $latest_slave
 && ( $check_replication_delay
 && $self->check_slave_delay( $_, $latest_slave ) >= 1 )
 )
 )
 {
 push( @ret_servers, $_ );
 }
 }

也是迴圈處理所有的配置的server,滿足下面三個條件之一就會被選擇放入bad陣列,也就說這些slave不會被推選為新的master。

d47e62d2b349aca45e42305ed6714efbe5ed61d9新增了引數no_master=1
d47e62d2b349aca45e42305ed6714efbe5ed61d9沒有開啟binlog

d47e62d2b349aca45e42305ed6714efbe5ed61d9如果延遲太大,如何才算是複製延遲太大呢?

( $latest->{Master_Log_File} gt $target->{Relay_Master_Log_File} )
|| ( $latest->{Read_Master_Log_Pos} >
$target->{Exec_Master_Log_Pos} + 100000000 )

這裡的latest就是上面選擇出來最靠前的第一個latest slave,不過所有的latest都是一樣的,所以選擇哪一個用於比較都是沒關係的。要麼latest的master_log_file > 對比者的Relay_Master_Log_File。或者是兩者相同,但是latest的Read_Master_Log_Pos > 對比者的Exec_Master_Log_Pos+1億。如果設定了引數check_repl_delay=0,那就不會會檢查複製延遲。

選擇slaves陣列

只要是活著的slave都會被放進slaves陣列當中。

這裡需要說明的是,一個slave可以放進多個陣列當中。不是一個slave只能存放到一個陣列當中。

第一次選擇:

return $latest[0] if ( $#pref < 0 && $#bad < 0 && $latest[0]->{latest_priority} );

如果pref和bad陣列當中slave的個數為0,則選擇latest陣列當中的第一個slave為master。

第二次選擇:

$log->info(
" Searching from candidate_master slaves which have received the latest relay log events.."
) if ( $#pref >= 0 );
foreach my $h (@latest) {
foreach my $p (@pref) {
if ( $h->{id} eq $p->{id} ) {
return $h
if ( !$self->get\_server\_from\_by\_id( @bad, $p->{id} ) );
}
}
}
$log->info(" Not found.") if ( $#pref >= 0 );

迴圈對比latest陣列和perf陣列的slave,如果存在相同的slave,並且這個slave不在bad陣列當中,該slave會被推選為新的master。

第三次選擇:

foreach my $s (@slaves) {
foreach my $p (@pref) {
if ( $s->{id} eq $p->{id} ) {
my $a = $self->get_server_from_by_id( @bad, $p->{id} );
return $s unless ($a);
}
}
}

迴圈對比slaves陣列pref陣列當中的slave,如果有一個slave相同並且不在bad陣列當中,該就會成為新的master。

第四次選擇:

foreach my $h (@latest) {
my $a = $self->get_server_from_by_id( @bad, $h->{id} );
return $h unless ($a);
}

迴圈latest陣列,如果有迴圈到的slave不在bad陣列當中,這個slave就會成為master。也就是說就算新增了candidate_master=1,該slave也不一定會成為主庫。

第五次選擇:

foreach my $s (@slaves) {
my $a = $self->get_server_from_by_id( @bad, $s->{id} );
return $s unless ($a);
}

從活著的slave當中進行迴圈,如果迴圈到的slave不在bad陣列當中,那麼這個slave就會成為主庫。 如果進行了5次選擇都找不到主庫,那麼主庫選擇失敗,failover失敗。

原文釋出時間為:2018-04-11

本文作者:魏新平

本文來自雲棲社群合作伙伴“老葉茶館”,瞭解相關資訊可以關注“老葉茶館”。


相關文章