引言:
上一期介绍了对于单个实例主备切换的涉及的业务细节,这次我们更深一步,讨论下真实场景中主库故障,或者网络出现故障时涉及到的问题。如果有不妥的地方,欢迎大家指正。
主库故障:
故障分类
一般的,我们会发现mysql 不可用的原因有几下几类:
1,主机硬件损坏,导致主机hang死,或者操作系统crash。此时客户端连接主机上的mysql进程时的表现是连接超时。因为不会回复ack包。此种情况与网络中断不能回包的表现是一样的,所以对于外部是无法判定是主机down还是网络故障的。我们遇到过raid 卡损坏,磁盘故障,电源模块损坏,起火等等。
2,操作系统配置或者环境问题,比如ipfilter 某个参数配置过小或者BUG,设置出错等,导致drop 掉某些包,会导致实例部分连接没有响应,但是主机可能还可以登录。此时与1的外部表现也是相同的。
3,mysql 自身BUG,或者某些异常 sql 导致mysql 自身crash,此种情况是mysql 进程crash,主机是完好的,所以可以通过连接主机其他端口验证出是进程故障还是主机故障。
4,mysql 实例性能问题,比如用户执行一个大事务,刷pageCache或者写log,导致磁盘IOUTIL 打满,此时表现是连接超时,或者连接可以建立,但是执行update语句超时。此时主库log 一般是可以正常传送到被库的。
针对以上故障,我们可能需要进行故障切换,故障切换最重要的两条原则,1)保证数据安全,保证数据可靠性。2)在一定时间内能快速响应,提高可用性。
可靠性保障:
可靠性概述
可靠性保障最重要的就是要确保被库与主库数据保持一致,进一步就是说主库写入的binlog都要能够传递到备库。在异步模式下,是无法保证的,在半同步模式下,大部分情况可以保证,为什么说是大部分情况,因为有可能是备库刚刚down机,启动后,正在IO线程追主库log,此时复制还是异步模式,但追上后,才会自动转为半同步模式。如果此时主库再down机,那么也一定会有部分log还在主库上。如果说要保证一定传到备库,保证数据强一致,那么当备库down机时,主库就不应该继续提供服务,此种用法在其他主流数据库中也可以设置,但是很少使用,因为对可用性影响较大,因为如果一旦备库down机,或者主备间网络断开,那么主库可用性立即就受到影响,这种对于一般用户来说是不可接受的。进一步,有人引入了一主多备模式,当有一个备库返回已经收到最后一条日志后,就可以给用户返回commit成功,这样提高了主库的可用性,同时也提高了成本。
言归正传,在一主一备的阿里云主流模式下,我们对于需要较高可靠性的用户,推荐使用半同步模式。集团内部的MHA系统提供了当主库故障时,从原主库同步log到备库,追上一段后,再提供服务的方案,此种方案能work的前提是原主库依然可以连接并且读取磁盘,并且会牺牲一段可用性时间,好处是可以补充一段binlog,让备库数据与主库一致。其实是可以作为一个比较好的补充手段。
RDS这里对用户实例做了24小时不间断的被库延迟监控,所以对于数据延迟的实例会提前报警,避免当主机完全不可恢复时,数据丢失。
##如何应对
考虑这样的场景,主备库分别部署在A,B两个机房做容灾,HA的两个节点也分别部署在两个机房,当A,B机房间发生网络故障,但是A,B机房自身正常时,两边的HA 都分别看见对端的实例节点放生故障,会将自己机房的实例提升为主库,那么此时如果两个机房都有流量进来,那么就可能导致数据库“双写”,也就是会发生著名的“脑裂”问题。
如果要解决“脑裂”问题,两个节点是不够的。那么我们引入了第三个节点,部署在另外一个机房,该节点无数据,只负责“脑裂”判定。这样构成了一个三个节点的三角模式(mongoDB,mssql 都是类似做法),三个点可以分别部署zookeeper 客户端并且选主,同一时刻,3个节点间只能有一个为leader。
3个节点中任意两个节点活着,那么实例可用。如果3个节点中两个或者3个节点挂掉,实例不可用。当A机房挂掉,或者实例在A机房的主机挂掉,那么leader 在B,C机房产生,此时由于B 机房可以连通leader 那么认为自己可以继续服务;C 机房挂掉,那么leader 在A,B中产生,A,B 都能连通leader ,那么仍然都可以继续服务。
网络故障场景
考虑网络故障情况,
A为主,B为备,C为裁判:
1)当A机房与B机房网络不通,3个节点都正常,A到C,B到C都正常。
leader 在A,那么A,C为多数派,A继续提供服务。
leader 在B,那么A摘除自己,B,C之间重新选择leader ,将B提升为新主库。
leader 在C,那么A, B服务不受影响,但是备库复制线程中断。
2)当A机房与C机房网络不通,
leader 在A,那么C 不能获取状态,摘除,B能与A leader 连接,继续work,A由于与B连通,投票多数,那么继续work ,不受影响。
leader 在B,那么A,C节点都与leader B可以通信,那么整体都可以work, 无任何影响。
leader 在C,那么A与C不通,那么A自己down掉自己,B与leader C通,所以B可以work , 将B中实例节点提升为主库。
3)当A与B,C机房都不通,
leader在A,B,C重新选leader ,那么A自己不work,然后B提升为主库。
leader在B或者C, 不必选leader , A自己不work,然后B提升为主库。
4)当B与C不通,与A通,B为备库,自己摘除自己,不影响可用。
leader在A,B与leader A通,那么不受影响。
leader在B,A和B构成多数派,继续提供服务。
leader在C,那么由于A与C通,B摘除自己,主库A继续提供服务。
5)当B与A,C都不通,A,C之间通,那么在A,C间重新选leader , 然后A继续提供服务。
6)当C与A,B 都不通,那么A,B将重新选择leader ,A继续提供服务。
当B 为主库时,分析与上面类似,综上,可以认为当主库本身节点与leader 节点不通,或者自己是leader节点,但是与另外两个节点都不通,自己成为少数派时,会发生failover,其余情况都可以正常工作。解决了“脑裂“问题,关键点就在于当节点自身发现是少数派时,自我管理,自己将自己杀掉。
同时,HA 按照正常逻辑提升备库为新主库,保证可用性。
#结合现实
回到现实中,因为条件有限,有些时候没办法都部署三机房,所以在两机房时,如果是C节点与B部署在一边,A节点此时是主库,那么当C所在的机房整体down机,那么数据库主节点A由于与leader节点不通,将自己关闭自己的写入,并且此时由于B与C所在节点down机,B也无法提供服务,那么此时实例就无法提供服务。
如果A节点主库与 B.C所在的机房网络不通,但是B,C机房可以提供服务,那么主库会自动failover到B节点,此时B和C选出一个leader , 继续提供服务,没有问题。
总结
本期主要从实际角度出发,阐述了一些场景,下一期会从2pc,3pc,分区一致性等理论角度来探讨下如何保证节点间的数据一致性。以及mysql 主库故障并且log 未传递到备库时,恢复备库可能的手段。