[toc]

MHA的简介

作者简介

松信嘉範:

MySQL/Linux专家

2001年索尼公司入职

2001年开始使用oracle

2004年开始使用MySQL

2006年9月-2010年8月MySQL从事顾问

2010年-2012年 DeNA

2012年~至今 Facebook

MHA的简介

MHA能够在较短的时间内实现自动故障检测和故障转移,通常在10-30秒以内;在复制框架中,MHA能够很好地解决复制过程中的数据一致性问题,由于不需要在现有的replication中添加额外的服务器,仅需要一个manager节点,而一个Manager能管理多套复制,所以能大大地节约服务器的数量;另外,安装简单,无性能损耗,以及不需要修改现有的复制部署也是它的优势之处。

MHA还提供在线主库切换的功能,能够安全地切换当前运行的主库到一个新的主库中(通过将从库提升为主库),大概0.5-2秒内即可完成。

MHA由两部分组成:MHA Manager(管理节点)和MHA Node(数据节点)。MHA Manager可以独立部署在一台独立的机器上管理多个Master-Slave集群,也可以部署在一台Slave上。当Master出现故障时,它可以自动将最新数据的Slave提升为新的Master,然后将所有其他的Slave重新指向新的Master。整个故障转移过程对应用程序是完全透明的。

MHA解析

MHA的工作原理

image-20230423160628563

  1. 把宕机的master二进制日志保存下来。
  2. 找到binlog位置点最新的slave。
  3. 在binlog位置点最新的slave上用relay log(差异日志)修复其它slave。
  4. 将宕机的master上保存下来的二进制日志恢复到含有最新位置点的slave上。
  5. 将含有最新位置点binlog所在的slave提升为master。
  6. 将其它slave重新指向新提升的master,并开启主从复制。

MHA的架构

image-20230423160821269

MHA工具

  • Manager节点工具

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@db01 ~]# tar xf mha4mysql-manager-0.56.tar.gz

    # 查看manager工具包含的命令
    [root@db01 ~]# ls -1 /root/mha4mysql-manager-0.56/bin/
    masterha_check_repl # 检测主从复制
    masterha_check_ssh # 检测ssh免密状态
    masterha_check_status # 检测MHA启动状态 systemctl statusmha
    masterha_conf_host # MHA切换之后,将宕机主库的配置从MHA配置文件中摘除
    masterha_manager # MHA启动工具 systemctl start mha
    masterha_master_monitor # MHA心跳检测工具
    masterha_master_switch # MHA故障切换工具
    masterha_secondary_check # MHA建立TCP连接
    masterha_stop # MHA的停止工具 systemctl stop mha
  • Node节点工具

    1
    2
    3
    4
    5
    6
    # 查看node工具包含的命令
    [root@db01 ~]# ls -1 /root/mha4mysql-node-0.56/bin/
    apply_diff_relay_logs # 对比从库上的relay-log
    filter_mysqlbinlog # 截取binlog、relay-log
    purge_relay_logs # 删除relay-log的工具
    save_binary_logs # 保存所有binlog事件

MHA优点总结

  1. Masterfailover and slave promotion can be done very quickly

    自动故障转移快

  2. Mastercrash does not result in data inconsistency

    主库崩溃不存在数据一致性问题

  3. Noneed to modify current MySQL settings (MHA works with regular MySQL)

    不需要对当前mysql环境做重大修改

  4. Noneed to increase lots of servers

    不需要添加额外的服务器(仅一台manager就可管理上百个replication)

  5. Noperformance penalty

    性能优秀,可工作在半同步复制和异步复制,当监控mysql状态时,仅需要每隔N秒向master发送ping包(默认3秒),所以对性能无影响。你可以理解为MHA的性能和简单的主从复制框架性能一样。

  6. Works with any storage engine

    只要replication支持的存储引擎,MHA都支持,不会局限于innodb

部署MHA

环境准备

主机名 LanIP WanIP 角色 MySQL版本 安装程序
db01 172.16.1.51 10.0.0.51 Node数据节点,主库 MySQL5.7.40 MySQL、Node
db02 172.16.1.52 10.0.0.52 Node数据节点,从库 MySQL5.7.40 MySQL、Node
db03 172.16.1.53 10.0.0.53 Node数据节点,Manager管理节点,从库 MySQL5.7.40 MySQL、Node、Manager
db04 172.16.1.54 10.0.0.54 Node数据节点,从库 MySQL5.7.40 MySQL、Node

做MySQL主从复制

  • MHA前提条件

    1. 主库要开启binlog,从库也要开启binlog
    2. 主库要创建主从复制用户,从库也要创建主从复制用户
    3. 主库的server_id要跟从库之间不同,从库之间也不可以相同
  • 主库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # 1.修改配置文件
    vim /etc/my.cnf
    [mysqld]
    basedir=/app/mysql
    datadir=/app/mysql/data
    log-bin=mysql-bin
    server_id=1

    # 2.重启数据库
    [root@db01 ~]# /etc/init.d/mysqld restart

    # 3.主库和从库都创建主从复制用户
    mysql> grant replication slave on *.* to rep@'172.16.1.%'identified by '123';

    # 4.记录binlog名字和位置点
    mysql> show master status;
    +------------------+----------+
    | File | Position |
    +------------------+----------+
    | mysql-bin.000001 | 447 |
    +------------------+----------+

    # 3.记录binlog的名字和位置点
    show master status;
  • 从库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    # 1.修改配置文件
    vim /etc/my.cnf
    [mysqld]
    basedir=/app/mysql
    datadir=/app/mysql/data
    log-bin=mysql-bin
    server_id=2/3/4

    # 2.重启数据库
    [root@db02 ~]# /etc/init.d/mysqld restart
    [root@db03 ~]# /etc/init.d/mysqld restart
    [root@db04 ~]# /etc/init.d/mysqld restart

    # 3.主库和从库都创建主从复制用户
    mysql> grant replication slave on *.* to rep@'172.16.1.%'identified by '123';

    # 4.执行change master语句
    change master to
    master_user='rep',
    master_password='Help9090',
    master_host='172.16.1.51',
    master_log_file='mysql-bin.000001',
    master_log_pos=447;

    # 5.开启主从复制
    start slave;

    # 6.查询主从复制状态
    show slave status\G

部署前准备

1
2
3
4
5
6
7
8
9
10
# 1.所有数据库关闭自动删除relay-log的功能(临时关闭)
set global relay_log_purge = 0;

# 2.所有数据库关闭自动删除relay-log的功能(永久关闭)
vim /etc/my.cnf
[mysqld]
relay_log_purge = 0 # 禁用relay-log自动删除功能

# 2.将所有从库设置为只读(临时设置)
mysql> set global read_only=1;

部署MHA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 1.所有数据库先安装node节点
yum localinstall -y http://test.driverzeng.com/MySQL_plugins/mha4mysql-node-0.56-0.el6.noarch.rpm

# 2.在manager服务器安装管理节点
yum localinstall -y http://test.driverzeng.com/MySQL_plugins/mha4mysql-manager-0.56-0.el6.noarch.rpm

# 3.所有数据库创建MHA管理用户(MySQL中,四台都要创建)
mysql> grant all on *.* to mha@'172.16.1.%' identified by 'mha';

# 4.查看已创建用户
mysql> select user,host from mysql.user;
+---------------+------------+
| user | host |
+---------------+------------+
| mha | 172.16.1.% |
| slave | 172.16.1.% |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+------------+

# 5.所有数据库做命令软链接
ln -s /app/mysql/bin/mysql /usr/bin/mysql && ln -s /app/mysql/bin/mysqlbinlog /usr/bin/mysqlbinlog

# 6.所有数据库创建密钥对
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1

# 7.所有数据库之间发送公钥(包括本身服务器)
ssh-copy-id -i ~/.ssh/id_dsa.pub 172.16.1.51
ssh-copy-id -i ~/.ssh/id_dsa.pub 172.16.1.52
ssh-copy-id -i ~/.ssh/id_dsa.pub 172.16.1.53
ssh-copy-id -i ~/.ssh/id_dsa.pub 172.16.1.54


# 8.配置MHA配置文件
[root@db03 ~]# mkdir /etc/mha
[root@db03 ~]# vim /etc/mha/app1.cnf
[server default]
manager_log=/etc/mha/app1/log/manager.log
manager_workdir=/etc/mha/app1
master_binlog_dir=/app/mysql/data
user=mha
password=mha
ping_interval=2
repl_password=123
repl_user=slave
ssh_user=root
ssh_port=22

[server1]
hostname=172.16.1.51
port=3306

[server2]
#candidate_master=1
#check_repl_delay=0
hostname=172.16.1.52
port=3306

[server3]
hostname=172.16.1.53
port=3306

[server4]
hostname=172.16.1.54
port=3306

# 9.创建MHA的工作目录和日志目录
[root@db03 ~]# mkdir /etc/mha/app1/log -p

# 11.手动测试ssh免密
[root@db03 ~]# masterha_check_ssh --conf=/etc/mha/app1.cnf
All SSH connection tests passed successfully.

# 12.手动测试主从复制情况
[root@db03 ~]# masterha_check_repl --conf=/etc/mha/app1.cnf
MySQL Replication Health is OK.

# 13.启动MHA
[root@db03 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /etc/mha/app1/log/manager.log 2>&1 &

## 选项解析
nohup
masterha_manager
--conf=/etc/mha/app1.cnf
--remove_dead_master_conf # 从mha配置文件中删除宕机主库的配置
--ignore_last_failover # 忽略上一次,最新一次故障转移
< /dev/null
> /var/log/mha/app1/manager.log
2>&1 &

MHA工作机制:做完一次切换后,会在工作目录下生成一个锁文件,锁文件会存在8小时
总结:MHA做完一次切换后,8小时之内,不会做第二次切换

# 14.检查mha状态
[root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
app1 (pid:20045) is running(0:PING_OK), master:172.16.1.51

MHA高可用的运行日志解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 查看mha运行日志
cat /etc/mha/app1/log/manager.log

# 检测是否是基于GTID的主从复制
GTID failover mode = 0

# VIP脚本
master_ip_failover_script is not set. Skipping invalidating dead master IP address

# MHA如何保存binlog
save_binary_logs \
--command=save \
--start_file=mysql-bin.000001 \
--start_pos=1028 \
--binlog_dir=/app/mysql/data \
--output_file=/var/tmp/saved_master_binlog_from_172.16.1.51_3306_2023 0424083240.binlog \
--handle_raw_binlog=1 \
--disable_log_bin=0 \
--manager_version=0.56

[root@db01 ~]# ll /var/tmp/
-rw-r--r-- 1 root root 177 Apr 24 08:32 saved_master_binlog_from_172.16.1.51_3306_20230424083240.binlog

# 没有检测到优先选举主库的配置
Candidate masters from the configuration file:
Non-candidate masters:
1.数据相同情况下,所有从库都有可能提升为主库
2.如果没有设置优先选主的参数,那么server标签越小优先级越高

# 其他从库加入集群,执行下面change语句 (很重要)
All other slaves should start replication from here. Statement should be:
CHANGE MASTER TO MASTER_HOST='172.16.1.52',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154,
MASTER_USER='slave',
MASTER_PASSWORD='123';

恢复MHA集群

  • manager节点服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    # 1.在MHA日志中找到change master语句
    grep -i 'change master to' /etc/mha/app1/log/manager.log
    Mon Apr 24 08:32:46 2023 - [info] All other slaves should start replication from here. Statement should be:
    CHANGE MASTER TO
    MASTER_HOST='172.16.1.52',
    MASTER_PORT=3306,
    MASTER_LOG_FILE='mysql-bin.000001',
    MASTER_LOG_POS=154,
    MASTER_USER='rep',
    MASTER_PASSWORD='xxx';

    # 2.在manager节点服务器补全MHA配置文件
    vim /etc/mha/app1.cnf
    [server default]
    manager_log=/etc/mha/app1/log/manager.log
    manager_workdir=/etc/mha/app1
    master_binlog_dir=/app/mysql/data
    password=mha
    ping_interval=2
    repl_password=Help9090
    repl_user=rep
    ssh_port=22
    ssh_user=root
    user=mha

    [server1]
    hostname=172.16.1.51
    port=3306

    [server2]
    hostname=172.16.1.52
    port=3306

    [server3]
    hostname=172.16.1.53
    port=3306

    [server4]
    hostname=172.16.1.54
    port=3306

    # 3.启动MHA
    [root@db03 ~]# nohup masterha_manager --conf=/etc/mha/app1.cnf --remove_dead_master_conf --ignore_last_failover < /dev/null > /etc/mha/app1/log/manager.log 2>&1 &

    # 4.检查mha状态
    [root@db03 ~]# masterha_check_status --conf=/etc/mha/app1.cnf
    app1 (pid:20045) is running(0:PING_OK), master:172.16.1.51
  • 宕机服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 1.修复宕机的主库并启动
    [root@db01 ~]# /etc/init.d/mysqld start

    # 2.修改密码在宕机主库执行
    CHANGE MASTER TO
    MASTER_HOST='172.16.1.52',
    MASTER_PORT=3306,
    MASTER_LOG_FILE='mysql-bin.000001',
    MASTER_LOG_POS=154,
    MASTER_USER='rep',
    MASTER_PASSWORD='Help9090';

    # 3.开启主从复制
    mysql> start slave;
  • 在manager节点服务器编写自动恢复宕机数据库的脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    vim mha.sh
    #!/bin/bash
    ## 数据库密码
    db_pass='Help9090'
    ## 宕机服务器IP
    mha_log_path='/etc/mha/app1/log/manager.log'
    down_master_ip=`sed -nr 's#^Master (.*)\(.*!#\1#gp' $mha_log_path`
    ## 获取在宕机服务器配置中配置的主库信息,并将'xxx'修改为密码
    change_statement=`grep -i 'change master to' ${mha_log_path} | awk -F: '{print $4}' | sed "s#xxx#${db_pass}#g"`

    # 启动宕机数据库
    ssh $down_master_ip '/etc/init.d/mysqld start'

    # 把宕机数据库配置为从库
    mysql -umha -pmha -h ${down_master_ip} -e "${change_statement};start slave"

    # 在manager节点服务器补全MHA配置文件
    cat > /etc/mha/app1.cnf <<EOF
    [server default]
    manager_log=/etc/mha/app1/log/manager.log
    manager_workdir=/etc/mha/app1
    master_binlog_dir=/app/mysql/data
    master_ip_failover_script=/etc/mha/app1/script/master_ip_failover
    password=mha
    ping_interval=2
    repl_password=Help9090
    repl_user=rep
    ssh_port=22
    ssh_user=root
    user=mha

    [server1]
    hostname=172.16.1.51
    port=3306

    [server2]
    hostname=172.16.1.52
    port=3306

    [server3]
    hostname=172.16.1.53
    port=3306

    [server4]
    #candidate_master=1
    #check_repl_delay=0
    hostname=172.16.1.54
    port=3306

    [binlog1]
    ## 不指定为master
    no_master=1
    ## 指定生效的主机
    hostname=172.16.1.53
    ## 指定binlog备份目录
    master_binlog_dir=/data/mysql/binlog/
    EOF

    MHA的VIP(Perl)

  • 主库

    1
    2
    # 手动给主库绑定VIP
    [root@db02 ~]# ifconfig eth1:1 172.16.1.55/24
  • manager节点数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # 1.创建脚本存放目录
    [root@db03 ~]# mkdir /etc/mha/app1/script

    # 2.下载脚本
    [root@db03 ~]# cd /etc/mha/app1/script
    [root@db03 script]# wget http://test.driverzeng.com/MySQL_File/master_ip_failover
    my $vip = '172.16.1.55/24';
    my $key = '1';
    my $ssh_start_vip = "/sbin/ifconfig eth1:$key $vip";
    my $ssh_stop_vip = "/sbin/ifconfig eth1:$key down";

    # 3.让MHA认识这个脚本(修改MHA配置文件)
    vim /etc/mha/app1.cnf
    master_ip_failover_script=/etc/mha/app1/script/master_ip_failover

    # 4.添加执行权限
    [root@db03 script]# chmod +x /etc/mha/app1/script/master_ip_failover

    # 5.转换格式
    [root@db03 script]# dos2unix /etc/mha/app1/script/master_ip_failover
    dos2unix: converting file /etc/mha/app1/script/master_ip_failover
    to Unix format ...
  • 脚本导致mha起不来的原因

    1. 语法问题
      • Perl
    2. 权限问题
      • 添加执行权限
    3. 格式问题
      • 转换为unix格式

MHA的binlog-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1.修改mha配置文件
[root@db03 ~]# vim /etc/mha/app1.cnf
[binlog1]
## 不指定为master
no_master=1
## 指定生效的主机
hostname=172.16.1.53
## 指定binlog备份目录
master_binlog_dir=/data/mysql/binlog/

# 2.创建存放binlog同步的目录
[root@db03 ~]# mkdir /data/mysql/binlog -p

# 3.MySQL自带同步binlog的工具 mysqlbinlog
[root@db03 ~]# cd /data/mysql/binlog/
[root@db03 binlog]# mysqlbinlog -R --host=172.16.1.55 --user=mha --password=mha --raw --stop-never mysql-bin.000001 &

MHA完整配置文件及解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[server default]
manager_log=/etc/mha/app1/log/manager.log
## manager的工作目录
manager_workdir=/etc/mha/app1
## manager的日志目录
master_binlog_dir=/app/mysql/data
## master保存binlog的位置,以便MHA可以找到master的日志,我这里的也就是mysql的数据目录
master_ip_failover_script=/etc/mha/app1/script/master_ip_failover
## 指定自动IP漂移的脚本
password=mha
## 监控用户的密码
ping_interval=2
## 监控主库,发送ping包的间隔(s),尝试三次没有回应的时候自动进行failover
repl_password=Help9090
## 主从用户的密码
repl_user=rep
## 主从用户
ssh_port=22
## ssh端口
ssh_user=root
## ssh连接用户
user=mha
## 监控用户

[server1]
hostname=172.16.1.51
port=3306

[server2]
hostname=172.16.1.52
port=3306

[server3]
hostname=172.16.1.53
port=3306

[server4]
candidate_master=1
## 设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中数据最新的slave
check_repl_delay=0
## 默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master
hostname=172.16.1.54
port=3306

[binlog1]
no_master=1
## 不指定为master
hostname=172.16.1.53
## 指定生效的主机
master_binlog_dir=/data/mysql/binlog/
## 指定binlog备份目录