MHA选择主库源码解析

导读

作者:魏新平 知数堂第5期MySQL实战班学员,第10期MySQL优化班学员,现任职助教。

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。

  1. 添加了参数no_master=1
  2. 没有开启binlog
  3. 如果延迟太大,如何才算是复制延迟太大呢?
 ( $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失败。

原文发布于微信公众号 - 3306pai(pai3306)

原文发表时间:2018-04-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏MasiMaro 的技术博文

Windows内核中的内存管理

其中PAGED_CODE是一个WDK中提供的一个宏,只在debug版本中生效,用于判断当前的中断请求级别,当级别高于DISPATCH_LEVEL(包含这个级别)...

13120
来自专栏androidBlog

java 解决文件名重复问题的两种方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

44610
来自专栏JavaWeb

Mybatis源码-XXXmapper.xml中的resultMap标签解析过程

17930
来自专栏伦少的博客

打印(获取)HDFS路径下所有的文件名(包括子目录下的)

自己有个需求,如题,需要获取HDFS路径下所有的文件名,然后根据文件名用Spark进行后续操作。想了一下用Spark好像不太容易获取到,还要递归的去获取子目录下...

33010
来自专栏Windows Community

Windows Community Toolkit 4.0 - DataGrid - Part01

在上面一篇 Windows Community Toolkit 4.0 - DataGrid - Overview 中,我们对 DataGrid 控件做了一个概...

12320
来自专栏wym

The 2018 ACM-ICPC Asia Qingdao Regional Contest, Online(四题签到)

11820
来自专栏图像识别与深度学习

蓝牙项目开发心得

34990
来自专栏TechBox

一份走心的iOS开发规范前言约定(一)命名规范(二)编码规范2.14 内存管理规范本文参考文章其他有价值的文章

64680
来自专栏小怪聊职场

爬虫课堂(二十七)|使用scrapy-redis框架实现分布式爬虫(2)源码分析

61860
来自专栏前端那些事

自制刻度尺插件-前端简易实现"腾讯信用"界面

依据我现有的知识,在前端上"简易"的实现了腾讯信用的界面,同时自己自制了一个竖直的刻度尺插件,曲线的位置可以根据传入的数值动态的改变,这次主要也想总结一下关于j...

332110

扫码关注云+社区

领取腾讯云代金券