魅族资深DBA:利用MHA构建MySQL高可用平台

龙启东

魅族资深DBA

  • 负责MySQL、Redis、MongoDB以及自动化平台建设 。擅长MySQL高可用方案、SQL性能优化、故障诊断等。

本次分享主要包括以下几方面:

  • 如何利用MHA

  • 改造MHA适应MySQL高可用场景

  • 构建MySQL高可用平台的出发点

  • 如何构建MySQL高可用平台

一、背景和目标

  1. 以前几十台DB服务器,人工登陆服务器就能维护好,也没有高可用,当master挂了,通知业务将IP切换到slave然后重启也能基本满足业务要求,但是业务迅速发展,实例数不断增加,复制集不断增加,数据库架构多样化,而这种人工维护方式显然大大增加了DBA工作量,而且效率低下、容易出错。

  2. DB规模的增大,机器故障、SQL故障、实例故障出现的概率也增加、还有来自业务方的DB变更,比如大表增加字段、增加索引、批量删除数据等异常维护操作,当然这些在一定条件下可用采用在线变更,比如采用pt-online-schema-change工具,但是当不满足在线变更条件、或者在线变更复杂的情况下,就需要采用滚动变更的方式,先在各个slave上变更、在线切换后再在master上变更,然后再进行一次切换还原,而这些切换操作如果全部手工敲命令来进行显然是不可取的。

  3. 随着用户数的不断增加,业务方对DB这种基础服务的可用性也就越来越高,在魅族业务对DB的可用性要求是每个月需要达到四个9,也就意味着每个月的故障时间只有不到5分钟,以前那种通知业务更改IP重启的方式显然是达不到这个要求的。

在这些背景和要求下,我们需要摆脱手工操作,需要一套有效的MySQL高可用方案和一个高效的高可用平台来支撑DB的快速增长。MySQL高可用平台需要达到的目标有以下几点:

  1. 数据一致性保证这个是最基本的同时也是前提,如果主备的数据的不一致,那么切换就无法进行,当然这里的一致性也是一个相对的,但是要做到最终一致性。

  2. 故障快速切换,当master故障时这里可以是机器故障或者是实例故障,要确保业务能在最短时间切换到备用节点,使得业务受影响时间最短。这里也可以指业务例行维护操作,比如前面提到的无法使用在线进行DDL的DDL操作,很多分表批量的DDL操作,这些操作通过在线切换方式来滚动完成。

  3. 简化日常维护,通过高可用平台来自动完成高可用的部署、维护、监控等任务,能够最大程度的解放DBA手动操作,提高日常运维效率。

  4. 统一管理,当复制集很多的情况下,能够统一管理高可用实例信息、实例信息、监控信息、切换信息等。

  5. 高可用的部署要对现有的数据库架构无影响,如果因为部署高可用,需要更改或者调整数据库架构则会导致成本增加。

  6. 魅族RDS平台需要高可用支持,这个高可用平台也是作为RDS的一个子模块,为RDS提供高可用支持、切换等服务。

如上图所示,通过测试比较几个候选方案,最终我们选择采用MHA作为线上MySQL高可用方案,主要基于如下几点:

  1. 自动探测,多重检测

  2. 部署简单,对现有架构无影响

  3. 自动补齐数据,维护数据一致性

  4. 切换过程中支持调用其他脚本的接口

  5. 支持在线切换

  6. 开源,perl编写,二次开发相对较容易

  7. 多实例集中管理

而这些特点中,支持调用外部脚本接口和在线切换尤为重要,当然perl编写,二次开发相对容易,可以根据自己业务需求适当的增加功能也不是难事。

二、MHA对业务访问方式控制

DB高可用其实可以分为两部分:DB访问方式控制、数据一致性保证,访问方式控制用于确保DB的服务可用性,而数据一致性则决定DB服务的数据可用性,两者是紧密结合在一起的,而高可用设计也是围绕这两点展开。

一般来说,当DB发生故障时,业务方是不希望通过调整DB连接地址来进行故障切换的,调整DB连接一般都会涉及到业务重启,如果说业务机器数据较多,重启时间则会较长,必然会增加业务的异常时间,也就意味着在业务中配置的DB地址是一个固定的连接,而DB的故障切换要做到对业务透明,无需重启,后端的DB恢复业务能够自动重连即可。通常来说,要对业务透明,像域名、vip、Zookeeper、Proxy这些访问方式都能做到,在魅族这边,业务的访问DB的方式有两种,分别是vip和自研的MySQL Proxy。但是不管是vip也好还是MySQL Proxy也好,对于一个业务来说都是指定了一个固定地址,这个地址一般来说都会一直伴随着这个业务,而高可用系统只要维护这个地址和后端的真实DB的映射关系就可以。

vip访问访问方式架构如下:

vip是在master机器上额外增加一个虚拟IP,而且这个IP是可以在master和standby之间漂移的,主要适用的场景是单机单实例,业务直连DB,切换是以机器为单位进行切换,但是需要master、standby在同一个网段。

MySQL Proxy访问方式架构如下:

MySQL Proxy的方式是在业务和DB之间增加一层中间件,业务连接的是中间件,而不直连后端真实DB,适用的场景为单机多实例,而且master,standby可以不在同一个网段,切换是以实例为单位进行。

在单机多实例下为何不采用vip呢,单机多实例下当一个机器上的一个实例发生故障,而其他实例是正常的,如果采用vip的话,这时候vip是漂移呢还是不漂移?如果漂移,那么其他正常的实例也会有短暂的影响,如果不漂移,那么该异常实例对应的业务异常时间就会增加,而且当该机器上其他实例的standby分布在不同机器上时,vip是无法漂移的。因此在单机多实例部署下采用Proxy,业务连接的是Proxy,而不是DB,而且业务也不需要知道是单机多实例还是单机单实例,而我们只需要维护Proxy和后端真实实例的映射关系即可。

我们知道MHA本身是不做业务访问方式控制的,MHA只负责master存活检测、选择new master、差异数据补齐,slave自动同步到new master,也就是尽可能做到数据一致性保证。因此我们需要找一个有效管理控制访问方式的方案来和MHA结合,使之同时满足高可用的两部分:DB访问方式控制、数据一致性保证。

如何控制要访问方式,对于vip访问方式来说,可以采用keeepalive来控制其漂移,当master异常,keepalive会自动将vip漂移到standby,但是这个方案在实际使用中会存在如下问题:

  1. 增加维护成本,对于每一套复制集下面的主备机器上都需要部署keepalive组件,当复制集众多的情况下,维护工作量很大。

  2. keepalive自身的在一定场景下的脑裂问题,对于只允许master有写入的前提下这样会导致数据异常,这对业务来说是不可接受的。

  3. 当master在并发写入很大的情况下,由于复制的单线程导致standby SQL线程还没有追上所有的relay log,这种情况下master异常,vip会在很短的时间内(我们测试3s)漂移到standby,这时候业务则会连接到standby并开始写入,而实际上standby还在应用relay log,这种情况下很有可能导致数据异常,这点在我们压测下遇到过。

  4. 单机多实例的情况下不适用,也就是之前说的,一个实例异常而其他实例正常的情况是不适合采用vip的,无法确定vip漂移到哪台机器。

既然keepalive不合适,那采用何种方案来控制业务访问DB方式呢,我们知道MHA在切换流程中提供了一个调用外部脚本的接口,当切换流程进行到指定步骤后,可以调用一个外部的脚本,至于外部脚本用于做什么事,MHA是不关注的,只关注调用的返回值,脚本逻辑完全自定义,因此我们可以在脚本中加入MHA本身无法满足的一些逻辑,比如这里说的控制业务访问方式等。也正是因为这个特点我们采用自定义外部脚本来控制业务访问方式,这种方式有如下优点:

  1. 不依赖外部组件,也就不会增加额外的运维成本。

  2. 自定义访问控制逻辑,而且在访问方式变更前和变更后可以增加一些额外的前置和后置检测逻辑,以确保访问方式切换后业务能正常访问。

  3. 由MHA调用,可以和MHA切换流程紧密的结合在一起,什么时间点调用依赖于MHA的切换进度,可控性更强,而不像keepalive一样分开为独立的两部分逻辑。

但是采用外部脚本来控制访问方式又会带来另外一个问题:根据MHA的配置,外部脚本由master_ip_failover_和master_ip_online_change_参数决定,一个用于自动切换,一个用于手动切换,而每个复制集的访问方式不管是vip还是mysql proxy都是固定的,因此就需要在脚本中指定该固定的访问信息,当MySQL复制集很多的情况下,每个复制集一个访问方式控制脚本,这就导致这种访问方式控制脚本很多,如果需要变更逻辑或者增加逻辑都需要变更所有的脚本,管理维护起来很不方便,而且很容了遗留和出错,但是我们不难发现,实际上这些脚本中除了每个业务配置的访问方式不同,其他逻辑都是一样的。既然这样,那我们是否可以将访问方式和MHA实例的配置绑定,将每个业务对应的访问地址写入到MHA实例的配置文件中,再将其控制逻辑剥离出来统一到一个脚本,调用的时候将该访问地址传递给该脚本使用即可。因此我们的解决方案如下:

  1. 在MHA配置文件中增加一个app_vip的参数,这里的app_vip参数可以是vip地址,也可以是mysql proxy的信息,这里的mysql proxy信息配置的是一个zookeeper节点信息,而不是业务配置的proxy ip:port,在mysql proxy模式下,是通过这个zookeeper节点来维护该proxy和后端的mysql实例的映射关系的,这里和vip有一点差异。

  2. 修改MHA调用外部脚本的接口,在接口中增加app_vip的参数,通过接口调用可以将该app_vip参数传递到外部脚本中。通过这个简单的改动,使得原来每个MHA实例需要一个脚本的方式变成所有MHA实例共用一个脚本,更便于维护管理。

那外部脚本在MHA切换流程的哪一步调用呢,这里以自动切换为例(手动切换一样),如下图所示:

切换流程可以简单归结为五步:

  • phase 1:切换前的配置检查,主要从配置文件中获取到该MHA实例下所有MySQL实例以及主从关系、候选节点标记、是否延时等等信息,

  • phase 2:关闭dead master,这里是确保master不可访问,在这一步中会调用外部脚本,调用状态为stop或者stopssh,这一步完成之后,要确保app_vip是不可用的。

  • Phase 3: 新的master恢复,将选择为新master的数据恢复到最新状态,这一步同样会调用外部脚本,调用状态为start,这一步完成之后,确保app_vip是可用状态,DB访问恢复正常。

  • Phase 4:将其他slave的数据恢复到最新状态以及将复制关系配置到新的master。

  • Phase 5:发送切换报告等其他一些收尾操作。

在整个切换流程中,会有两处调用到外部脚本,对这两个接口都需要增加app_vip参数,每次调用需要将app_vip参数传入到外部脚本。

具体实现比较简单,能看懂perl语法的话问题都不大,MHA参数都在Config.pm中定义,在参数列表PARAM_ARRAY中增加app_vip,然后在后面初始化即可,如下图所示:

然后还需要将app_vip这个参数添加到MHA调用外部脚本的接口中,这里涉及到MHA自动切换、手动切换和启动阶段,这三部分功能分别位于MasterFailover.pm、MasterMonitor.pm和MasterMonitor.pm中,这里以自动切换MasterFailover.pm中代码为例,在自动切换过程中,会有两个地方存在调用外部脚本,Phase 2: Dead Master Shutdown Phase..和Phase 3.4: Master Log Apply Phase.对于Phase 2: Dead Master Shutdown Phase..接口在force_shutdown_internal函数中定义,对于Phase 3.4: Master Log Apply Phase. 只需要在recover_master 函数中做接口调整。

简单如下图:

现在看看外部脚本如何实现,至于采用什么语言实现都可以,只需要实现满足调用接口即可,具体的接口参数可以参考MHA源码中的示例代码,我们采用python完成。由于我们这里访问方式有两种,而两种访问方式完全不同,因此对于两种访问方式的控制逻辑也完全不同,vip的控制逻辑比较好理解,app_vip参数配置的是一个具体的vip。master异常包括两种情况,如果是实例异常,则将vip在master机器摘下,确保失效后,绑定到standby即可,如果是机器宕机,则vip会自动实效。

而对于MySQL Proxy就稍微复杂一些,app_vip参数配置的是一个zookeeper节点,每个复制集在Zookeeper上有一个唯一的节点,Proxy和后端MySQL实例的映射关系是在Zookeeper上维护的。当master异常,更改Zookeeper上proxy和后端master的映射关系后,会自动通知到MySQL Proxy,Proxy再将转发打到新的master,采用MHA后,这个映射关系由外部脚本来控制。

外部脚本中实现了两套控制逻辑,根据传入的参数是虚拟IP还是zookeeper节点来选择具体的控制逻辑,而每个控制逻辑对应三个command状态:status、stop or stopssh、start.

  • status只在MHA启动的时候调用,从MHA的代码来看,作用是测试该外部脚本是否有效,而我们可以用于检测该app_vip是否是正常状态,绑定关系是否正常,这里主要检查app_vip和master的关系映射是否正常。

  • stop or stopssh在MHA切换流程中调用,确保app_vip是失效状态,业务不可写入,这一步在手动切换中更有效,在自动切换中,master发生故障已经不可写入了,这里只是再多check一次而已。

  • start 在MHA切换流程中调用,将app_vip和MHA选择的new master绑定,这一步执行完之后,确保app_vip是正常访问状态,业务新的请求全部打到new master。

这里为何要确保app_vip是失效状态或者是正常状态,这个Proxy的实现机制有关,对于MySQL Proxy来说,脚本操作的是Zookeeper节点,而MySQL Proxy收到通知再到变更会有一个短暂的时间差,理论上是非常快的。对于手动操作来说,这里简化一下切换流程如下:先调用外部脚本stop操作,再执行FLUSH TABLES WITH READ LOCK操作,再将standby切换为master,再调用外部脚本的start操作,然后执行master上的UNLOCK TABLES操作,如果Zookeeper和MySQL Proxy之前的网络出现波动就会导致MySQL Proxy收到通知延时,在master上执行UNLOCK TABLES操作后,而此时MySQL Proxy的转发还是在原来的master,而此时切换已经完成,有部分数据业务方写入了,而实际上是丢失了,因为写入是slave角色(原来的master目前是slave),而业务显示是写入是正常的。

为了避免这种情况出现,就需要在每一步调用后,确保app_vip参数是否满足预期状态,对于MySQL Proxy来说,就要确保后端的master和Proxy的映射关系是否正常,而不仅仅是更改了Zookeeper信息就可以。对于vip访问方式来说,绑定操作如下:ip addr add xxx/32 dev xxx,摘除操作如下:ip addr del xxx。将vip绑定到新的master机器,通常要验证本网段之外的其他网段访问该vip是否正常,通过情况下,只是简单的将vip 绑定到new master,其他网段在短时间内是不通的,为了使vip能够迅速生效,需要做一次arping操作,否则对于其他网段访问,会有一定的生效延时。

前面是访问方式控制上实现,再来看看我们在其他两个点上的改动。

  • 第一:对于每一个复制集来说,只能是master可以写入,而slave全部要确保是read only = ON,以避免业务配置错误将数据写入到slave。MHA在启动的时候会检测一次各个slave是否设置了read only=ON,对于read only = OFF的实例只是warning提示一下,并不会去强制设置read only=ON。在这里我们增加一个步骤,当检测到slave为read only = OFF的,就将该实例设置为read only=ON,但是设置read only=ON对于线上已经运行的实例来说可能会导致堵塞,从而导致MHA实例启动堵塞,当该实例有长事务执(比如大表逻辑备份操作)的时候,执行set global read_only=ON就会导致堵塞,需要等待长事务执行完成,而且进而导致slave上sql thread堵塞,从而导致slave复制延时。为了避免这种情况,因此在为该实例设置read only之前,先检测该slave实例上是否有长事务在执行,如果有则跳过read only=ON的设置,先优先启动MHA实例。而对于这些没设置read only=ON的实例,会额外增加一个任务来定时检测,检测线上每一个slave是否设置了read only=ON,没有则设置上。这样可以在很大程度上避免因为业务配置错误导致slave产生写入从而导致主从数据不一致的情况出现。

  • 第二:master检测失败次数加大,MHA默认的失败检测次数是3次,当连续三次检测master异常,则开始判断是否需要发起切换,而我们设置的检测间隔是1s,也就意味着3s时间内master不通,则会判断是否发起切换,3s时间太短,很容易因为网络波动等原因导致master误切换,因此我们将失败检测次数提高到10次。而且为了避免MHA管理机和master网段之间不通导致的切换,我们还增加了一个外部脚本secondary_check_,发起切换检测前,该脚本会从其他多个网段来检测是否和master都无法连通了来确定master是否真正需要切换,上面两点也是为了最大程度避免误切带来的业务异常。

三、MHA管理平台

当线上只有一套或者几套复制环境时,即使手工操作就可以运维下来,但是当线上复制环境有几百套,而且架构多样,跨多个机房的时候,再用手动部署和运维就显得很费时费力,容易出错而且效率很低。目前我们这边的DB现状如下:

  1. 实例数500+

  2. 复制集200+

  3. 架构多样

  • 一主一从

  • 一主多从

  • 一主多从跨机房多从

  • 单机单实例

  • 单机多实例

而我们面临的问题如下:

  1. 部署麻烦、耗时

  2. 增加一套复制集忘记部署高可用

  3. 增加一个slave忘记添加到MHA

  4. 减掉一个slave忘记在MHA中去掉

  5. slave调整没有同步MHA配置

  6. 主动切换麻烦

  7. MHA manager节点不断增多

拿部署一套MHA来说,需要完成下图中的所有流程:

而且必须保证每一个流程都是正确的,通常人工部署会遇到如下问题:

  1. 需要登录每台机器进行操作

  2. 需要建立ssh信任连接

  3. 需要确认复制状态

  4. 手动操作过于繁琐

  5. 容易出错

而且部署每套MHA涉及的操作也是类似的,也就说明DBA在这些工作上都是重复耗时的,而这些流程基本已经固定了,每次部署按部就班即可。因此我们可以将这些流程自动化,让整个部署操作自动完成,以给一套一主三从复制集部署高可用耗时来计算至少可以节约DBA 80%的时间。因此我们迫切需要一个MHA管理平台来帮助解决我们所遇到的问题,MHA管理平台设计的主要的功能点如下:

  1. MHA信息、MySQL实例信息统一管理

  2. 快速部署MHA

  3. 启动、停止MHA进程

  4. 增减slave自动同步到MHA配置

  5. MySQL实例变更

  6. 手动切换

  7. MHA回收

  8. 主从切换后角色同步

  9. 告警管理

这些功能点基本涵盖了DBA在MHA上的运维操作,通过将这些运维操作平台、自动化,以最大程度的较少DBA运维时间、提高运维效率和减少运维误操作。

MHA管理平台整体架构如下:

MHA管理平台直接操作的是MHA manager,实际使用中会存在多个MHA manager,当然在部署的时候也会操作到MHA node,对于MHA node来说,MHA manager是服务端,而对于MHA管理平台来说,就是客户端。每个MHA manager可以管理多套MHA实例,我们预设的数量是50,每个MHA manager最多管理50个MHA实例,也就是50套复制集。

MHA管理平台还需要和RDS平台打通,作为RDS的高可用子模块,RDS的高可用需要依赖MHA管理平台,在一定条件下,两者的信息是相互调用的。MHA管理平台和告警平台相连主要是为了告警信息的处理。对于一套复制环境中实例跨多个机房的情况,我们选择master所在机房的MHA manager做为管理节点,而且standby也必须在同一个机房,正常情况下,master只会在同机房进行切换,而对于复制环境需要跨机房容灾也是支持的,这就需要在两个机房先将MHA配置在MHA manager上同步好,确保在两个机房的MHA manager上都能完全操作该MHA实例,不过跨机房切换中需要考虑的因素太多,目前只支持手动切换,尚不支持自动切换,这一点在后续需要进一步完善。

下面介绍几个平台中的主要操作页面,这是MHA部署的页面:

相比于手工操作要登录每台机器,这里只需要填写很小一下信息提交即可,信息验证通过之后,会以任务的形式在后台部署。具体包括如下工作:MHA信息,MySQL实例信息入库,ssh信任连接建立,配置文件生成,选择mha manager,复制状态检查,MHA启动。对于机房的选择和前面说的一样,选择和master实例在同一个机房,访问方式有两种:vip和MySQL Proxy,而我们目前都在往RDS上迁移,因此从RDS上请求过来的部署都是选择MySQL Proxy,而vip目前只是用来满足原有的单机单实例业务进行扩容,对于新业务来说不再提供vip的方式接入,全部采用RDS实例。

我们线上的实例版本主要有两个:5.5.9和5.7.13,5.5.9下我们全部采用MHA 0.54版本,而5.7.13 则采用MHA 0.56版本,在0.56版本支持GTID和table方式存放relay 信息,而在MHA 0.54下会出现异常。这里只需要选择MySQL版本即可后台会根据版本自动根据MHA版本来选择MHA manager。该页面主要是为了兼容vip的部署方式,而对于MySQL Proxy的方式,都是从RDS平台进行调用该页面对应的接口即可,在RDS上申请一套RDS实例是会自动包含高可用的,不需要额外的部署高可用。

MHA列表页:

该页面显示每个MHA管理节点,以及该MHA manager节点下管理的MHA实例列表,可以列出该mha manager管理下的所有MHA实例信息,包括集群名,MHA进程信息,运行状态(运行中、已停止、部署中、正在切换等)

MHA实例详情页:

该页面显示指定的MHA实例详情,包括MHA实例所在的MHA管理节点,复制集名称,访问类型,MHA运行状态,该MHA实例下的所有MySQL实例是否正常,slave状态是都正常等等。而对于MHA的所有日常运维操作也全部在这个页面完成,比如:启停MHA实例,增减slave,slave信息变更,在线切换、复制检查,ssh检查等操作。所有的这些操作都需要有对应的接口,主要提供给RDS平台调用,而这些功能在RDS平台的逻辑单元管理下面都有对应的操作,也就不用两个平台来回切换使用,该页面主要为了兼容非RDS上的实例。

在线切换操作是远程调用masterha_master_switch来完成的,为了保证切换的顺利进行,采用非交互的方式进行interactive=0,而且在切换前做前置条件检查,这里主要是复制延时检查,ssh互通检查,master长事务检查,只有满足在线切换前提才发起在线切换操作。在线操作默认的日志输出是在屏幕,这对于远程调用而言,为了便于定位问题需要将日志保存,保存在指定的workdir目录下。这里的代码调整比增加app_vip参数稍微复杂一点,但是可以参考自动切换的流程来进行调整。原来的流程是在identify_orig_master()函数中拿到所有的实例信息,并选择出orig_master,在这里我们增加一个函数init_config()用于获取所有的实例信息,并且初始化日志信息,返回值为所有的实例信息,然后将返回值传递给identify_orig_master()函数,而在identify_orig_master()函数中去掉获取实例信息的代码。这样就能确保原有的逻辑不变而且增加了日志记录功能,在线切换日志需要和自动切换日志区分开来,增加一个switch_online后缀即可。

四、MHA状态同步和监控

为何要做MHA状态监控和MySQL角色同步?随着大规模的部署,在运维过程中就会遇到各种各样的问题,主要问题如下:

  1. MHA进程异常退出了为何没告警

  2. 切换后MySQL角色怎么没同步

  3. MHA切换失败怎么没告警

  4. MHA部署失败怎么没告警

  5. Slave下线导致MHA切换失败

  6. 增加一个slave没有同步到MHA

  7. 业务异常才发现切换失败

  8. 切换进度不可控

  9. masterha_check_status不完善

而这其中有不少问题是会直接影响到高可用或者数据一致性的,比如MHA进程异常退出了而没有告警,DBA也一直不知道,而到了master异常,才发现没有切换,再一查才发现MHA进程早就停止了。有时候master发生了切换,但是MySQL角色在RDS平台上没有同步更新,这就导致RDS上数据和实际的角色不一致。还有因为某些原因将一个slave下线了,数据也清理了,但是没有在MHA配置中同步,从而导致当MHA发生切换时连接该实例发生异常导致整个切换流程失败。还有在一个复制环境下,增加一个slave,或者slave信息变更没有同步到MHA配置中,发生切换后导致该实例的复制异常,等等以上原因都会影响到业务的可用性。

此外切换进度不透明,我们只知道发起了切换,但是不通过看日志的话,切换进行到哪一步了根本不知道,无法做到完全可控。为了最大程度的避免上述问题,就需要提前发现和处理掉这些问题,对于异常情况及时告警,对于上述出现的问题,针对每一项做针对性的跟踪和监控,有些只需要告警即可,而有些需要根据场景做特定的处理。我们的解决方案如下:

  1. MHA是否正常运行,状态跟踪

  2. 切换步骤实时跟踪

  3. 切换后MySQL角色同步变更

  4. MHA状态告警

  5. MHA切换短信告警、邮件告警

  6. MHA部署流程告警

  7. 定时检测MHA配置一致性

  8. 访问方式检测

MHA状态如何跟踪?我们首先看一下MHA实例的生命周期,如下图所示:

这里从MHA实例启动开始,先做启动初始化,主要是配置检查,包括复制检查,ssh检查等等,有异常则直接退出,启动失败,正常则启动成功,此时MHA状态为PING_OK,然后开始对master进行ping监控,注意这里是没有对slave做监控的。当master异常时,ping失败,这里MHA的状态为PING_FAILING,然后连续ping master,当失败次数大于10次后,MHA的状态为PING_FAILED,这时候会再做一次master check,确认master是否已经dead,如果此时是alive状态,MHA的状态为RETRYING_MONITOR,重新对master进行ping监控,如果此时确实是dead状态,则发起切换流程,此时MHA的状态为FAILOVER_RUNNING,切换流程之前已经说过大致可以分为五步,如果切换过程中发生异常,MHA的状态为FAILOVER_ERROR,生成切换失败文件,以failover.error为后缀,如果切换正常完成,则生成一个failover.complete后缀的文件。这些状态码在ManagerConst.pm中已经定义好,如下图所示:

而我们则可以根据这些状态来判断MHA的具体的运行状态,我们只需要将这些状态码上报到MHA管理平台,在服务端做针对性处理即可。除了这些MHA定义的状态之外,为了更准确的跟踪MHA切换流程,还需要在在状态为FAILOVER_RUNNING的五步中,定义更细致的子状态用于跟踪切换流程的具体进度,以便于根据切换流程。定义了状态码之后,就需要在MHA切换逻辑中增加这些状态码上报的逻辑,具体实现如下:

  1. MHA管理平台首先需要定义好对应的接口,用于接收这些状态,并定义具体的状态码所对应的具体后续操作。

  2. 在MHA配置文件中增加mha_ip,mha_oss_api两个参数(具体实现和增加app_vip一样),mha_ip是该mha实例所在的mha manager的ip地址,mha_oss_api为MHA管理平台接口地址,MHA实例用appname和mha_ip组合作为全局标识

  3. MHA状态定义,除了MHA自身带的状态码,还需要增加其他一些状态码,比如deploy, deploy failed, initializing,running,stop,failover switch begin,online switch begin,failover switch complete,online switch complete,failover_runing等。

  4. 在MHA代码中增加状态码上报的逻辑,主要涉及到文件有MasterFailover.pm和MasterRotate.pm,将上报的逻辑嵌套在切换流程中即可,这里以自动切换为例,自动切换代码位于MasterFailover.pm中,主要的切换流程位于函数do_master_failover(),在该函数中有整个切换流程的各个子流程,比如我要上报切换开始的状态,则可以在Phase 1之前增加如下上报代码:

  5. change_mha_status()函数则是增加的用于上报状态的接口,而其他子流程中都可以做类似的状态上报,还可以做其他的一些判断检查,有一点需要注意的是,嵌入的代码不能影响原有的切换流程。MHA自身的状态变更都在FileStatus.pm中完成更新,因此自身的状态上报可以在该文件增加增加上报逻辑。

  6. 为了监控MHA进程异常退出的情况,增加了一个后台任务,定时监控所有的MHA进程是否是正常运行的,并对监控结果做告警处理。

此外我们针对性的做了MHA配置一致性监控,为何要做MHA配置一致性监控? 前面也提到了几点:增减slave实例没有同步到MHA配置中,slave信息变更没有同步到MHA配置中,因为某些原因切换时发先ssh信任关系是失败的,这些因素要么导致MHA切换失败,要么导致MHA发生切换后有slave数据和新的master数据不一致而且复制关系异常,不管是哪一种结果,对于业务来说都是不可接受的,因此我们需要提前发现这些问题。增加如下任务:

  1. 定时检测MHA配置中复制状态,ssh信任状态,检测异常及时告警到DBA或者做自动修复,前面提到MHA是只ping master的,因此这个任务完善了对各个slave实例的状态监控。

  2. 定时检测MHA配置和线上真实的复制架构,有新增的slave,自动同步到MHA配置中,有slave剔除的,根据具体的探测结果进行告警或者做同步MHA配置处理,这里的逻辑稍微复杂一些,不能简单的检测到slave不存在则在MHA中剔除,也有可能是临时的维护。

做这些操作是为了尽可能多角度的监控MHA实例状态,以确保master异常时,MHA切换流程能正常的完成,而不是等到切换开始后才发现切换流程无法完成。

增加了监控还需要做告警处理,告警处理主要分为两种:短信和邮件,对于需要DBA干预的,尽可能快速的通过短信方式同步到DBA侧,对于只需要DBA知会的,只做邮件通知即可,具体的告警场景如下所示:

  1. 状态上报后根据状态码生成告警及时同步到DBA侧

  2. 切换开始状态上报后,根据开始时间计算判断切换时间

  3. 在指定时间内没有切换完成状态上报,则告警到DBA侧

  4. 切换完成后,将复制集状态同步到DBA侧

  5. 自动切换完成后,将切换结果同步邮件到DBA侧

  6. 部署成功、失败信息同步到DBA侧

  7. MHA实例异常退出告警到DBA侧

  8. vip可用性、mysql proxy可用性告警

  9. MHA配置与复制集不一致告警

这其中需要说明的是,当切换开始后,会上报切换开始的状态码,这时候需要在指定的时间内判断是否接收到切换完成或者切换失败的状态码,用于判定切换是否失败或者正常完成,避免在某些情况下,切换流程hold住导致切换流程长时间无法完成,从而影响DB服务时间。

对于vip可用性、mysql proxy可用性的告警,是做了一个额外的任务来完成,定时检测所有的vip或者proxy的可用性并根据结果做告警处理。这里的告警看似很多,但是很多都是很有必要的,我们应该尽可能多的将MHA的状态及时的同步到DBA侧,以便于在出现异常情况时,DBA更加及时的进行干预和处理。

五、未来发展

目前该平台已经运行了大半年时间,运行稳定,不过还存在有待完善地方,有如下几点:

  1. 因为需要捕获差异日志,目前MHA还是需要依赖ssh信任关系来进行差异日志拷贝。一方面会存在安全隐患,登录到其中一台机器可以无需认证调到其他机器。二者做ssh信息关系比较耗时,因此这点需要做优化,接入专门的运维通道,摆脱对ssh信任关系的依赖

  2. MHA切换流程在一些情况下会导致切换中途失败,这时候要么人为干预继续往下切换完成,要么回滚,而回滚目前来说根据切换进度不同缺乏对应的回滚方法,基本上全部需要人工来操作,这无疑是增加了故障时间,在这一点上后续需要进行改进,在切换失败的情况下,根据切换进度和人为干预结果,快速的往下切换完成,或者快速的回滚。

  3. 在MHA切换中加入更多的状态采集,以便更加细致的跟踪和定位切换流程。

Q&A

Q1:这个只有故障切换功能,没有负载均衡,读写分离?

A1:有的,MySQL Proxy架构下面只有挂载了读的slave,就会将select转发过去的,如果没有slave(不是standby)则全部请求master。而对于vip的方式,扩展读用的是LVS。

Q2:是否有遇到在高并发下,切换有时会失败?

A2:失败没遇到过,如果是在线切换的话,时间会延长不少。

Q3:魅族自研的MySQL Proxy,是中心式结构,像mycat。还是分布式结构,像TDDL。最大集群的分片是多少?

A3:都不是的,MySQL Proxy目前的功能只是处理转发,起到一个过载保护的作用,我们在Proxy上可以做一些统计以及审计的工作。分片我们这边是业务自己做的,最大分片集群的有70多组。

Q4:对于主从同步失败造成的主从不一致如何检测和修复?

A4:这种不一致,我们会定时的采用pt工具去check一次。如果数据不一致情况严重,会选择重做。

Q5:MySQL proxy做负载均衡的话,是按BATCH请求,还是按数据库connection连接来做的?

A5:connection连接来做的。

Q6:老师您好!我这在使用MySQL 数据库遇到一些问题,还望各位老师能给些意见。背景如下:MySQL 是 1主3从,每台机器一个数据库4个实例,总共16台机器,前4台是主库,后12台是从库,但数据插入到主库,往从库同步时特别慢,1900万数据几个小时还没有同步完,后台只有一个线程在同步,单核cpu达到100%,我们的48核cpu根本没用上。

A6:如果是5.5版本的话,复制是单线程的,主库写入很大的情况下,从库需要一定的时间才能追上。从库在复制上是用不到多核的。如果有多个数据库的话,可以采用5.6版本或者5.7版本。复制性能会好不少。

Q7:老师,我们使用的5.7.16的版本。我们同步的一张大表的数据,整个库中也就几张大表。

A7:如果是一张表的话,性能不会有多大改善,建议做大表拆分。

Q8:请问该系统的自动化运维如何实现的?

A8:django + celery + fabric。

Q9:如何有效管理版本升级及补丁发布?

A9:对于MySQL Proxy而言有专门的版本管理和发布平台,对于MHA版本来说,目前有0.54和0.56,都是基于这两个版本来改的。

Q10:备份如何实施的?

A10:针对MySQL备份开发了备份恢复的模块,目前采用的分布式的全量备份,副本保存30天,目前也在开发基于binlog的增量备份。

Q11:如何和CMDB结合应急处理的?

A11:每个复制集所对应的业务都是从CMDB中获取的。

posted @ 2019-04-19 17:22  zping  阅读(721)  评论(0编辑  收藏  举报