MHA笔记

简介

MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中,MHA能做到在0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。

该软件由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群,也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。

在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制,可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性。

目前MHA主要支持一主多从的架构,要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,一主二从,即一台充当master,一台充当备用master,另外一台充当从库,因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝TMHA已经支持一主一从。

工作原理

MHA工作原理如下:

  1. 从宕机崩溃的master保存二进制日志事件(binlog events);
  2. 识别含有最新更新的slave;
  3. 应用差异的中继日志(relay log)到其他的slave;
  4. 应用从master保存的二进制日志事件(binlog events);
  5. 提升一个slave为新的master;
  6. 使其他的slave连接新的master进行复制;

MHA软件由两部分组成,Manager工具包和Node工具包,具体的说明如下。
Manager工具包主要包括以下几个工具:

masterha_check_ssh              检查MHA的SSH配置状况
masterha_check_repl             检查MySQL复制状况
masterha_manger                 启动MHA
masterha_check_status           检测当前MHA运行状态
masterha_master_monitor         检测master是否宕机
masterha_master_switch          控制故障转移(自动或者手动)
masterha_conf_host              添加或删除配置的server信息

Node工具包(这些工具通常由MHA Manager的脚本触发,无需人为操作)主要包括以下几个工具:

save_binary_logs                保存和复制master的二进制日志
apply_diff_relay_logs           识别差异的中继日志事件并将其差异的事件应用于其他的slave
filter_mysqlbinlog              去除不必要的ROLLBACK事件(MHA已不再使用这个工具)
purge_relay_logs                清除中继日志(不会阻塞SQL线程)

安装部署

具体的搭建环境如下:

角色                    ip地址          主机名          server_id                   类型
Monitor host            192.168.0.20    server01            -                      监控复制组
Master                  192.168.0.50    server02            1                      写入
Candicate master        192.168.0.60    server03            2                      读
Slave                   192.168.0.70    server04            3                      读

创建用户:

chattr -i /etc/group
chattr -i /etc/gshadow
chattr -i /etc/passwd
chattr -i /etc/shadow

useradd -m -d /home/mha mha
passwd mha

chattr +i /etc/passwd
chattr +i /etc/shadow
chattr +i /etc/group
chattr +i /etc/gshadow

修改权限:

#visudo
末尾添加:
mha ALL=(ALL) NOPASSWD: ALL

加入root组:
## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
mha     ALL=(ALL)       ALL

创建、下发公钥,已MHA Server为例:

ssh-keygen -t rsa
ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.0.50
ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.0.60
ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.0.70

修改mha用户环境变量,添加perl、mha、mysqlbinlog:

#vim /home/mha/.bashrc
export PTAH=$PATH:/usr/share/perl5:/opt/soft/mha/bin/:/opt/soft/mysql57/bin/
export LC_ALL=C

修改对应目录权限:

chown -R mha.mha /opt/soft/mha/*
chown -R mha.mha /usr/share/perl5/vendor_perl/MHA/*

安装MHA:

#mha server
rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm
rpm -ivh mha4mysql-manager-0.56-0.el6.noarch.rpm

#mha node
rpm -ivh mha4mysql-node-0.56-0.el6.noarch.rpm

rpm包是重新打包的,安装后环境如下:

#mha server
#tree /opt/soft/mha/
/opt/soft/mha/
|-- bin
|   |-- apply_diff_relay_logs
|   |-- filter_mysqlbinlog
|   |-- masterha_check_repl
|   |-- masterha_check_ssh
|   |-- masterha_check_status
|   |-- masterha_conf_host
|   |-- masterha_manager
|   |-- masterha_master_monitor
|   |-- masterha_master_switch
|   |-- masterha_secondary_check
|   |-- masterha_stop
|   |-- purge_relay_logs
|   `-- save_binary_logs
|-- binlog
|-- conf
|   |-- app1.cnf
|   `-- masterha_default.cnf
|-- logs
|   `-- log
`-- scripts

#mha node
#tree /opt/soft/mha/
/opt/soft/mha/
└── bin
    ├── apply_diff_relay_logs
    ├── filter_mysqlbinlog
    ├── purge_relay_logs
    └── save_binary_logs

添加配置文件:

#masterha_default.cnf
[server default]
user=mha
password=mha
ssh_user=mha

repl_user=repl
repl_password=repl

manager_workdir=/opt/soft/mha
manager_log=/opt/soft/mha/logs/log
master_binlog_dir=/work/mysql6666/var/
remote_workdir=/work/dba/
secondary_check_script=/opt/soft/mha/bin/masterha_secondary_check -s 192.168.0.60 -s 192.168.0.70 --user=mha --master_ip=192.168.0.50 --master_port=6666

# master_ip_failover_script=/opt/soft/mha/scripts/master_ip_failover
# master_ip_online_change_script=/opt/soft/mha/scripts/master_ip_online_change
# report_script=/opt/soft/mha/scripts/send_report
# shutdown_script=/opt/soft/mha/scripts/power_manager

ping_interval=1

#app.conf
[server1]
hostname=192.168.0.50
port=6666

[server2]
hostname=192.168.0.60
port=6666
candidate_master=1
check_repl_delay=0

[server3]
hostname=192.168.0.70
port=6666
no_master

检查SSH配置:

/opt/soft/mha/bin/masterha_check_ssh --global_conf=/opt/soft/mha/conf/masterha_default.cnf --conf=/opt/soft/mha/conf/app.cnf

检查复制环境:

/opt/soft/mha/bin/masterha_check_status --global_conf=/opt/soft/mha/conf/masterha_default.cnf --conf=/opt/soft/mha/conf/app.cnf

开启mha监控

/opt/soft/mha/bin/masterha_manager --global_conf=/opt/soft/mha/conf/masterha_default.cnf --conf=/opt/soft/mha/conf/app.cnf --ignore_last_failover

脚本含义

Manager

  • masterha_check_ssh:检查mha的ssh配置情况;
  • masterha_check_repl:检查MySQL复制情况;
  • masterha_manager:启动mha;
  • masterha_check_status:检查当前mha运行状态;
  • masterha_master_monitor:检测master是否宕机;
  • masterha_check_switch:控制故障转移(自动或手动);
  • masteha_conf_host:添加或删除配置的server信息;

Node

  • save_binary_logs:保存和复制master的二进制文件;
  • apply_diff_relay_logs:识别差异的relay log并将其差异event应用于其他slave;
  • filter_mysqlbinlog:去除不必要的rollback event;
  • purge_relay_logs:清除relay log;

工作流程

masterha_manager

mha启动脚本为masterha_manager,可选参数为remove_dead_master_conf、manger_log、ignore_last_failover。
masterha_manager主要流程为:

  1. 调用MasterMonitor,监控MySQL master状态;
  2. 发现master状态异常后,调用MasterFailover进行切换;

具体细节如下:

my ( $exit_code, $dead_master, $ssh_reachable ) =
  MHA::MasterMonitor::main( "--interactive=0", @ARGV );

manager通过monitor监测master状态,一旦获得返回值,则表明monitor状态异常。通过判断exit_code确定是否应切换。

if ( $exit_code && $exit_code != $MHA::ManagerConst::MASTER_DEAD_RC ) {
  exit $exit_code;
}
if ( !$dead_master->{hostname}
  || !$dead_master->{ip}
  || !$dead_master->{port}
  || !defined($ssh_reachable) )
{
  exit 1;
}

检测通过后,调用MasterFailover进执行切换操作。

$exit_code = MHA::MasterFailover::main(
  "--master_state=dead",
  "--interactive=0",
  "--dead_master_host=$dead_master->{hostname}",
  "--dead_master_ip=$dead_master->{ip}",
  "--dead_master_port=$dead_master->{port}",
  "--ssh_reachable=$ssh_reachable",
  @ARGV
);
MasterHA_Manager.png

MasterMonitor

MasterHA_Manager调用MasterMonitor的main方法对MySQL进行监控。流程图如下:


MasterMonitor.png

核心方法是一个死循环,不断调用wait_until_master_is_dead方法监测主库状态,部分代码如下:

while (1) {
  my ( $exit_code, $dead_master, $ssh_reachable ) =
    wait_until_master_is_dead();
  my $msg = sprintf( "Got exit code %d (%s).",
    $exit_code,
    $exit_code == $MHA::ManagerConst::MASTER_DEAD_RC
    ? "Master dead"
    : "Not master dead" );
  $log->info($msg) if ($log);
  if ($g_check_only) {
    finalize();
    return $exit_code;
  }
  if ( $exit_code && $exit_code == $RETRY ) {
    prepare_for_retry();
  }
  else {
    if ( $exit_code && $exit_code != $MHA::ManagerConst::MASTER_DEAD_RC ) {
      finalize_on_error();
    }
    elsif ($g_monitor_only) {
      finalize();
    }
    return ( $exit_code, $dead_master, $ssh_reachable );
  }
}

wait_until_master_is_dead方法的返回值中,exit_code有的值有四种,分别是0、1、20、retry。其中只有当exit_code=MHA::ManagerConst::MASTER_DEAD_RC,也就是20时,后续才会调用failover方法。

wait_until_master_is_dead方法中,核心方法是调用wait_until_master_is_unreachable方法并处理其返回值。逻辑关系如下:


wait_until_master_is_dead.png

拿到wait_until_master_is_unreachable的返回值后,会再次根据配置文件探活,确认主库连接失败后,根据配置文件检测slave状态和数量,有合适新主库后,exit_code返回20,否则返回0或者1;

再次检测代码存活的代码如下:

$_server_manager->connect_all_and_read_server_status(
  $dead_master->{hostname},
  $dead_master->{ip}, $dead_master->{port} );
my @dead_servers  = $_server_manager->get_dead_servers();
my @alive_servers = $_server_manager->get_alive_servers();
$log->info("Dead Servers:");
$_server_manager->print_dead_servers();
$log->info("Alive Servers:");
$_server_manager->print_alive_servers();
$log->info("Alive Slaves:");
$_server_manager->print_alive_slaves();
$_server_manager->print_failed_slaves_if();
$_server_manager->print_unmanaged_slaves_if();

wait_until_master_is_unreachable方法的返回值有三个,分别是ret、dead_master和ssh_reachable。该方法的逻辑如下:


wait_until_master_is_unreachable.png

wait_until_master_is_unreachable调用MHA::ServerManager对主库进行实时检测,包括deadservers、aliveservers、aliveslaves等。

$_server_manager = new MHA::ServerManager( servers => \@servers_config );
$_server_manager->set_logger($log);$_server_manager->connect_all_and_read_server_status();
@dead_servers  = $_server_manager->get_dead_servers();
@alive_servers = $_server_manager->get_alive_servers();
@alive_slaves  = $_server_manager->get_alive_slaves();
$log->info("Dead Servers:");
$_server_manager->print_dead_servers();
$log->info("Alive Servers:");
$_server_manager->print_alive_servers();
$log->info("Alive Slaves:");
$_server_manager->print_alive_slaves();
$_server_manager->print_failed_slaves_if();
$_server_manager->print_unmanaged_slaves_if();
$current_master = $_server_manager->get_current_alive_master();

如果启用GTID,则检查binlog server,否则进行ssh和slave版本检测。

    if ( !$_server_manager->is_gtid_auto_pos_enabled() ) {
      $log->info("GTID (with auto-pos) is not supported");
      MHA::SSHCheck::do_ssh_connection_check( \@alive_servers, $log,
        $servers_config[0]->{log_level}, $g_workdir )
        unless ($g_skip_ssh_check);
      $log->info("Checking MHA Node version..");
      foreach my $slave (@alive_slaves) {
        MHA::ManagerUtil::check_node_version(
          $log,             $slave->{ssh_user}, $slave->{ssh_host},
          $slave->{ssh_ip}, $slave->{ssh_port}
        );
      }
      $log->info(" Version check ok.");
    }
    else {
      $log->info(
"GTID (with auto-pos) is supported. Skipping all SSH and Node package checking."
      );
      check_binlog_servers( $binlog_server_ref, $log );
    }

后续使用MHA::HealthCheck对主库进行ping检查,如果定义了secondary_check_script,则运行该脚本。检查确认主库的确不可达后,返回func_rc, current_master, ssh_reachable。

$master_ping = new MHA::HealthCheck(
  user                   => $current_master->{user},
  password               => $current_master->{password},
  ip                     => $current_master->{ip},
  hostname               => $current_master->{hostname},
  port                   => $current_master->{port},
  interval               => $current_master->{ping_interval},
  ssh_user               => $current_master->{ssh_user},
  ssh_host               => $current_master->{ssh_host},
  ssh_ip                 => $current_master->{ssh_ip},
  ssh_port               => $current_master->{ssh_port},
  ssh_connection_timeout => $current_master->{ssh_connection_timeout},
  ssh_check_command      => $ssh_check_command,
  status_handler         => $_status_handler,
  logger                 => $log,
  logfile                => $g_logfile,
  workdir                => $g_workdir,
  ping_type              => $current_master->{ping_type},
);
$log->info(
  sprintf( "Set master ping interval %d seconds.",
    $master_ping->get_ping_interval() )
);
if ( $current_master->{secondary_check_script} ) {
  $master_ping->set_secondary_check_script(
    $current_master->{secondary_check_script} );
  $log->info(
    sprintf( "Set secondary check script: %s",
      $master_ping->get_secondary_check_script() )
  );
}
else {
  $log->warning(
"secondary_check_scriptis not defined. It is highly recommended setting it to check master reachability from two or more routes."
  );
}

添加vip检测功能

config.pm负责mha的参数处理,如果想添加vip检测参数,则可在my @PARAM_ARRAY 后添加master_vip和master_vip_port。并在parse_server函数中添加解析:

$value{master_vip} = $param_arg->{master_vip};
$value{master_vip_port} = $param_arg->{master_vip_port};

检查vip状态函数可放在MasterMonitor.pm中:

sub check_vip_status {
  my @servers_config;
  my ( $sc_ref, $binlog_server_ref ) = new MHA::Config(
    logger     => $log,
    globalfile => $g_global_config_file,
    file       => $g_config_file
  )->read_config();
  @servers_config = @$sc_ref;
  my $master_vip = $servers_config[0]->{master_vip};
  my $master_vip_port = $servers_config[0]->{master_vip_port};
  my $user = $servers_config[0]->{user};
  my $password = $servers_config[0]->{password};
  my $logfile = $servers_config[0]->{manager_log};
  $log = MHA::ManagerUtil::init_log($logfile);
  if ( !defined($master_vip) || !defined($master_vip_port))
  {
    $log->info("no master_vip or master_vip_port in config file,skip VIP check.") if ($log);
    return 2;
  }
  eval {
    my $dbhelper = DBI->connect("DBI:mysql:;host=$master_vip;" . "port=$master_vip_port;",$user,$password,{ PrintError => 0, RaiseError => 1 });
    my $sth = $dbhelper->prepare("SELECT 1 As Value");
    $sth->execute();
    my $href = $sth->fetchrow_hashref;
  };
  if ($@) {
    $log->info($@) if ($log);
    $log->info("connect VIP error...") if ($log);
    return 0;
  }
  else {
    $log->info("check VIP config success.") if ($log);
    return 1;
  }
}

对主库进行状态检查时,可以先检测vip状态,若vip存活,则进行下一轮检测。该逻辑可放置在MasterMonitor的main函数中。

while (1) {
    my $VIPStatus =  check_vip_status();
    if ($VIPStatus ne 0)
    {
        my ( $exit_code, $dead_master, $ssh_reachable ) = wait_until_master_is_dead();
        ......
    }
}

项目地址
未完待续……

哨兵实现多路探测
ETCD构建MHA Cluster
主库恢复后自动挂载
中途失败完全回滚
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容