MHA高可用
一 MHA背景介绍
MHA 是Perl语言写的,开源的MYSQL故障切换方案;全称:Master High Availability,故障切换时间10-30s
有人说,我不要MHA行不行啊; 可以,没问题, 如果主数据库故障了?
首先,你需要手动一个一个地登录上所有的SLAVE从库,然后一个个对比,看谁执行的BINLOG比较新,然后将其作为主库。
接下来,你还需要手动把所有的从库CHANGE MASTER到新选出的主库。
一个字“费劲”!
这要是别的事,也还可以忍,但主库挂掉,会导致业务无法访问,一堆人站你旁边看着你,到时候一紧张再忘记开写了、SLAVE挂不上去,这个影响时间就不是一星半点了;
为了解决上述问题,就有人写了个程序,来执行自动的主从故障切换,这就是MHA;
二 MHA简介
MHA(Master High Availability)是由日本人 yoshinorim(原就职于DeNA现就职于FaceBook)开发的一款成熟的、开源的 MySQL 的高可用程序。它为 MySQL 主从复制架构提供了自动故障切换( automating master failover) 功能。 MHA 在监控到 master 节点故障时,会提升其中拥有最新数据的 slave 节点成为新的master 节点,在此期间,MHA 会通过于其它从节点获取额外信息来避免一致性方面的问题,从而在最大程度上保证数据的一致性,以达到真正意义上的高可用,而且整个故障转移过程对应用程序完全透明。最值得称赞的一点是:这一自动故障切换操作,MHA能在10~30秒之内完成。
此外,MHA 还提供了 master 节点的在线切换功能,即按需切换 master/slave 节点,大概0.5-2秒内即可完成。
目前MHA主要支持一主多从的架构,要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器,例如一主二从。因为至少需要三台服务器,出于机器成本的考虑,淘宝也在该基础上进行了改造,目前淘宝的TMHA已经支持一主一从。
三 MHA工作原理
3.1 MHA的组成
MHA由node和manager组成;
- MHA Node(数据节点):
相当于监控客户端,所有数据库机器都需要部署node
-
MHA Manager(管理节点)
Manager相当于服务端,MHA Manager会定时探测集群中的master节点,当master出现故障时,它可以自动将最新数据的slave提升为新的master,然后将所有其他的slave重新指向新的master(如果原主库恢复,只能当从库)。
通常单独部署在一台独立机器上管理多个 master/slave 集群(组),每个 master/slave 集群称作一个 application,用来管理统筹整个集群。
Manager应该尽量避免部署在主库上,否则主机一挂则全挂,不仅主库完蛋了,负责自动迁移的Manager也完蛋了,也没人负责自动故障迁移了,导致架构不可用了。
可以考虑部署在某一个slave上,此时这台主机挂掉了,只是挂了一个slave,以及Manager,如果此时你不是倒了霉,(主库也挂了),那还不至于架构不可用。但有一点需要注意的是:如果Manager部署在slave上,那么该slave就无法被升级为主库;
由上图我们可以看出,每个复制组内部和 Manager 之间都需要ssh实现无密码互连,只有这样,在 Master 出故障时, Manager 才能顺利的连接进去,实现主从切换功能。
3.2 MHA自动故障切换的步骤
(1), Manager会每隔3秒钟探测一次MASTER主库; (Hi, 主库你还活着吗?)
ping_interval 控制间隔时间;
ping_type 控制探测方式,SELECT(执行SELECT 1)和CONNECT(创建连接/断开连接)
(2), 如果Manager探测到MASTER主库故障、无法访问,Manager会执行下面操作:
1、从其他node发起ssh连接,检查主库是否能够SSH上去;
2、从其他node发起mysql连接,检查MASTER库是否能够登陆;
(3), 如果所有Node节点ssh连接、msyql主库连接均连接失败,则开始故障转移,
简单地说
1.找到数据最新的从库(通过对比relay-log,查看show slave status即可)
2.将最新的从库上的新数据同步到其他从库
3.提升一个从库为主库(一般情况提升数据最新的,二般情况提升我们指定的从库为主库)
4.通过原来主库的binlog补全新的主库数据
5.其他从库以新的主库为主做主从复制
详细步骤如下:
Phase 1 | Configuration Check Phase.. | 检查数据库版本 检查是否启用GTID 检查从库是否存活 检查配置文件的candidate |
Phase 2 | Dead Master Shutdown Phase. | 该阶段会调用master_ip_failover脚本;去关闭所有Node的IO Thread 调用shutdown_script 强制关闭MASTER实例,防止应用程序来连接; |
Phase 3 | Master Recovery Phase.. | |
Phase 3.1 | Getting Latest Slaves Phase. | 检查所有节点,从show slave status中对比获取最新的binlog/position |
Phase 3.2 | Saving Dead Master’s Binlog Phase.. | 如果老的Master可以SSH,上去获取BINLOG,从position到END位置,获取这段BINLOG(MASETER产生这段BINLOG,还未来得及发送给SLAVE)将这部分日志发送给Manager节点(manager_workdir位置); 如果故障Master无法SSH,则无法获取这段日志 |
Phase 3.3 | Determining New Master Phase.. | 对比所有SLAVE,从最新SALVE中同步差异realy log给其他slave;最终确保所有SLAVE数据一致 |
Phase 3.3 | New Master Diff Log Generation Phase.. | 确认新master 是否为最新slave,如果不是,则从最新slave获取差异日志; 将manager上获取的BINLOG日志发送给new master; |
Phase 3.4 | Master Log Apply Phase.. | 对比新master的Exec_Master_Log_Pos和Read_Master_Log_Pos,判断恢复的位置; 在本地回放 3.3 Phase的差异日志; 获取新master的binlog和position; |
Phase 4 | Slaves Recovery Phase.. | |
Phase 4.1 | Starting Parallel Slave Diff Log Generation Phase. | 对每个SLAVE恢复:所有SLAVE和最新Slave做对比,如果position不一致,则生产差异日志 |
Phase 4.2 | Starting Parallel Slave Log Apply Phase. | 每个SLAVE 应用差异日志; 执行CHANGE MASTER 挂在到新Master |
Phase 5 | New master cleanup phase.. | reset slave all; |
四 MHA的优点总结
1、自动的故障检测与转移,通常在10-30秒以内;
2、MHA还提供在线主库切换的功能,能够安全地切换当前运行的主库到一个新的主库中(通过将从库提升为主库),大概0.5-2秒内即可完成。
3、很好地解决了主库崩溃数据的一致性问题
4、不需要对当前的mysql环境做重大修改
5、不需要在现有的复制框架中添加额外的服务器,仅需要一个manager节点,而一个Manager能管理多套复制,所以能大大地节约服务器的数量;
6、性能优秀,可以工作在半同步和异步复制框架,支持gtid,当监控mysql状态时,仅需要每隔N秒向master发送ping包(默认3秒),所以对性能无影响。你可以理解为MHA的性能和简单的主从复制框架性能一样。
7、只要replication支持的存储引擎都支持MHA,不会局限于innodb
8、对于一般的keepalived高可用,当vip在一台机器上的时候,另一台机器是闲置的,而MHA中并无闲置主机。
五 GTID主从复制
5.1 什么是GTID
在MHA自动故障切换过程中,MHA试图从宕机的主服务器上保存二进制日志,最大程度的保证数据的不丢失,但这并不总是可行的。例如,如果主服务器硬件故障或无法通过ssh访问,MHA没法保存二进制日志,只进行故障转移而丢失了最新的数据。使用自MySQL 5.5开始引入的半同步复制,可以大大降低数据丢失的风险。
MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志,MHA可以将最新的二进制日志应用于其他所有的slave服务器上,因此可以保证所有节点的数据一致性,或者采用GTID。
那什么是GTID呢???
从MySQL 5.6.2 开始新增了一种基于 GTID 的复制方式,用以替代以前传统的复制方式,到MySQL5.6.10后逐渐完善。通过 GTID 保证了每个在主库上提交的事务在集群中有一个唯一的ID。这种方式强化了数据库的主备一致性,故障恢复以及容错能力,那么它如何做到的呢?
要想在分布式集群环境中不丢失事务,就必须为每个事务定制一个全局唯一的ID号,并且该ID是趋势递增的,以此我们便可以方便地顺序读取、不丢事务,其实GTID就是一种很好的分布式ID实践方案,它满足分布ID的两个基本要求
1)全局唯一性
2)趋势递增
GTID (Global Transaction ID全局事务ID)是如何做到全局唯一且趋势递增的呢,它是由UUID+TID两部分组成
UUID是数据库实例的标识符
TID表示事务提交的数量,会随着事务的提交递增
#具体形式:5426a3c1-ade1-11e9-90b3-000c29bb4490:23
因此他与主库上提交的每个事务相关联,GTID不仅对其发起的服务器是唯一的,而且在给定复制设置中的所有服务器都是唯一的,即所有的事务和所有的GTID都是1对1的映射。
当在主库上提交事务或者被从库应用时,可以定位和追踪每一个事务,对DBA来说意义就很大了,我们可以适当的解放出来,不用手工去可以找偏移量的值了,而是通过CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION=1的即可方便的搭建从库,在故障修复中也可以采用MASTER_AUTO_POSITION=‘X’的方式。
5.7版本GTID做了增强,不手工开启也自动维护匿名的GTID信息
5.2 GTID主从的原理
(1)一个GTID的生命周期
1.事务在主库上执行并提交给事务分配一个gtid(由主库的uuid和该服务器上未使用的最小事务序列号),该GTID被写入到binlog中。
2.备库读取relaylog中的gtid,并设置session级别的gtid_next的值,以告诉备库下一个事务必须使用这个值
3.备库检查该gtid是否已经被其使用并记录到他自己的binlog中。slave需要担保之前的事务没有使用这个gtid,也要担保此时已分读取gtid,但未提交的事务也不恩呢过使用这个gtid.
4.由于gtid_next非空,slave不会去生成一个新的gtid,而是使用从主库获得的gtid。这可以保证在一个复制拓扑中的同一个事务gtid不变。由于GTID在全局的唯一性,通过GTID,我们可以在自动切换时对一些复杂的复制拓扑很方便的提升新主库及新备库,例如通过指向特定的GTID来确定新备库复制坐标。
(2)架构图
同样的GTID不能被执行两次,如果有同样的GTID,会自动被skip掉。
slave1:将自己的UUID1:1发送给master,然后接收到了UUID1:2,UUID1:3 event
slave2:将自己的UUID1:1,UUID1:2发送给master,然后接收到了UUID1:3事件
5.3 部署基于GTID的主从复制
注意:
? 主库和从库都要开启binlog
? 主库和从库server-id必须不同
? 要有主从复制用户
主:mysql5.6,192.168.15.100,配置如下
[mysqld]
。。。。。。。
server-id=100
binlog_format=row
log-bin=mysql-bin
skip-name-resolve # 跳过域名解析(非必须)
gtid-mode=on # 启用gtid类型,否则就是普通的复制架构
enforce-gtid-consistency=true #强制GTID的一致性
log-slave-updates=1 # slave更新是否记入日志(5.6必须的)
relay_log_purge = 0 # 关闭relay_log自动清除功能,保障故障时的数据一致
从1:mysql5.6,192.168.15.101,配置如下,只有server-id与主不同
[mysqld]
。。。。。。。
server-id=101
binlog_format=row
log-bin=mysql-bin
skip-name-resolve # 跳过域名解析(非必须)
gtid-mode=on # 启用gtid类型,否则就是普通的复制架构
enforce-gtid-consistency=true #强制GTID的一致性
log-slave-updates=1 # slave更新是否记入日志(5.6必须的)
relay_log_purge = 0 # 关闭relay_log自动清除功能,保障故障时的数据一致
从2:mysql5.6,192.168.15.102,配置如下,只有server-id与主不同
[mysqld]
。。。。。。。
server-id=102
binlog_format=row
log-bin=mysql-bin
skip-name-resolve # 跳过域名解析(非必须)
gtid-mode=on # 启用gtid类型,否则就是普通的复制架构
enforce-gtid-consistency=true #强制GTID的一致性
log-slave-updates=1 # slave更新是否记入日志(5.6必须的)
relay_log_purge = 0 # 关闭relay_log自动清除功能,保障故障时的数据一致
主库创建复制账户
mysql> GRANT REPLICATION SLAVE ON *.* TO egon@'%' IDENTIFIED BY '123';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
两个从库开启复制
change master to
master_host='192.168.15.100',
master_user='egon',
master_password='123',
MASTER_AUTO_POSITION=1;
启动从库的复制
start slave;
show slave status\G
show variables like '%gtid%';
测试主从
如果想制作双主,步骤如下
# 找一台从库,假设为192.168.15.101,在该主机上创建账号
GRANT REPLICATION SLAVE ON *.* TO egon@'%' IDENTIFIED BY '123';
flush privileges;
# 在主库上执行下述操作,指向从库192.168.15.101
change master to
master_host='192.168.15.101',
master_user='egon',
master_password='123',
MASTER_AUTO_POSITION=1;
# 开启主库的slave
start slave;
5.4 跳过事务
传统的复制例,如果主从复制遇到了某条sql错误,SQL停了,我们可以通过下述操作跳过错误
stop slave;
set global sql_slave_skip_counter=1;
start slave;
在传统的主从里,counter=1是跳过一条sql,而GTID是基于事务的主从复制,如果跳过就是跳过一整个事务,所以上述方法并不适用于GTID
#跳过事务的方法:
1)停止slave进程
mysql> STOP SLAVE;
2)设置事务号,执行show slave status\G查看Retrieved_Gtid_Set的值即事务号
在session里设置gtid_next,即跳过这个GTID
mysql> SET GTID_NEXT= '6d257f5b-5e6b-11e8-b668-5254003de1b6:1'
3)设置空事物
mysql> BEGIN; COMMIT;
4)恢复事物号
mysql> SET SESSION GTID_NEXT = AUTOMATIC;
5)启动slave进程
mysql> START SLAVE;
5.5 GTID优缺点总结
GTID相比传统复制的优点(新特性):
1、一个事务对应一个唯一ID,一个GTID在一个服务器上只会执行一次,强化了一致性
2、GTID是用来代替传统复制的方法,GTID复制与普通复制模式的最大不同就是不需要指定二进制文件名和位置,直接自动查找
3、GTID会开启多个SQL线程,每一个库,开启一个SQL线程
4、支持延时复制
GTID的限制:
1.不支持非事务引擎
2.不支持create table t1(…) select * from t2; 语句复制(主库直接报错)
原理:( 会生成两个sql,一个是DDL创建表SQL,一个是insert into 插入数据的sql。
由于DDL会导致自动提交,所以这个sql至少需要两个GTID,但是GTID模式下,只能给这个sql生成一个GTID )
3.不允许一个SQL同时更新一个事务引擎表和非事务引擎表
4.在一个复制组中,必须要求统一开启GTID或者是关闭GTID
5.开启GTID需要重启(5.7除外)
6.开启GTID后,就不再使用原来的传统复制方式
7.对于create temporary table 和 drop temporary table语句不支持
8.不支持sql_slave_skip_counter
9.mysqldump 备份起来很麻烦,需要额外加参数 –set-gtid=on
六 部署MHA
6.1 环境准备
机器名称 | IP地址 | 角色 | 备注 |
---|---|---|---|
manager | 192.168.15.200 | Manager控制器 | 用于监控管理 |
master | 192.168.15.100 | 主数据库服务器 | 开启binlog、relay-log,关闭relay_log_purge |
slave1 | 192.168.15.101 | 从1数据库服务器 | 开启binlog、relay-log,关闭relay_log_purge、设置read_only=1 |
slave2 | 192.168.15.102 | 从2数据库服务器 | 开启binlog、relay-log,关闭relay_log_purge、设置read_only=1 |
先基于上一小节完成基于GTID的一主两从复制,在做主从之前,要保证主从数据的一致性。
然后需要知道的是:
-
1、所有数据库服务器都需要开启binlog日志
-
2、所有数据库都需要有主从复制用户,但我们之前在主库上执行过下述命令,从库已经同步过去了,所以不必重复创建,重复创建了也没啥卵用
GRANT REPLICATION SLAVE ON *.* TO egon@'%' IDENTIFIED BY '123'; flush privileges;
-
3、需要在主库上创建一个管理用户,用于日后MHA的管理
# 在主库上执行即可,从库会同步过去 grant all on *.* to 'mhaadmin'@'%' identified by '666'; flush privileges;
-
4、MHA 对 MYSQL 复制环境有特殊要求,例如各节点都要开启二进制日志及中继日志,各个“slave节点”必须显式启用3其
read-only
属性,并关闭relay_log_purge
功能等。所以我们需要#1、在从库上进行操作 #设置只读,不要添加配置文件,因为从库以后可能变成主库 mysql> set global read_only=1; #2、在所有库上都进行操作(这一步我们在5.3小节已经做过了,此处忽略即可) #关闭MySQL自动清除relaylog的功能 mysql> set global relay_log_purge = 0; #编辑配置文件 [root@mysql-db02 ~]# vim /etc/my.cnf [mysqld] #禁用自动删除relay log永久生效 relay_log_purge = 0
6.2 配置免密登录(所有主机之间互做)
配置免密登录(每一台机器都执行下述命令)
#创建秘钥对
#正常创建 ssh-keygen 需要交互 按回车,用以下方法跳过交互
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa >/dev/null 2>&1
#发送公钥,包括自己
ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.15.200
ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.15.100
ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.15.101
ssh-copy-id -i /root/.ssh/id_dsa.pub root@192.168.15.102
6.3 安装软件包
安装依赖(所有机器)
# 安装yum源
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
# 安装MHA依赖的perl包
yum install -y perl-DBD-MySQL perl-Config-Tiny perl-Log-Dispatch perl-Parallel-ForkManager
注意先后顺序
先在所有主机上安装node包
wget https://qiniu.wsfnk.com/mha4mysql-node-0.58-0.el7.centos.noarch.rpm --no-check-certificate
rpm -ivh mha4mysql-node-0.58-0.el7.centos.noarch.rpm
然后再去manager主机安装manager包(manager机器,192.168.15.200)
wget https://qiniu.wsfnk.com/mha4mysql-manager-0.58-0.el7.centos.noarch.rpm --no-check-certificate
rpm -ivh mha4mysql-manager-0.58-0.el7.centos.noarch.rpm
6.4 配置MHA Manager
(1)在Manager主机上创建工作目录
mkdir -p /service/mha/
mkdir /service/mha/app1