<span>主从复制系列C</span>

近日接到一个故障,主从异步方式,主 crash后,从不可用,检查发现从机Read_Master_Log_Pos与Exec_Master_Log_Pos不一致,似乎还有binlog在回放中,HA在等回放结束,一直保持这个状态。难道从机也出故障了?根本原因是什么?且看下文。

MySQL binlog简介

首先简单了解要下binlog日志,Binary Log是在MySQL3.23.14中引入的,记录MySQL数据修改记录的文件集合。

Binary Log有两个目的:

用于复制。master(主)上的binlog日志发送到slave(从)上,slave执行回放master上的修改动作,保持主从数据一致。

用于数据恢复。对binlog日志文件指定始末位置执行即恢复指定数据。

本文是针对复制流程中的一个改进,不涉及数据恢复。

基于binlog的复制简介

Master上对修改动作生成binlog,Slave机IO拉取对应的binlog到本地生成relay log,然后Slave机SQL线程回放执行。binlog的最小单位为event,master与slave之间以event为单位传输日志,一个或多个event组成一个事务,slave机上以事务为单位回放日志。事务是数据库的常见基本特性,不再介绍,这里主要介绍下binlog中的event。

事务event包含header 和data,Header包含有event的类型,时间,哪个server产生等信息。data有对应类型event的细节,如特定的数据变化。

每个binlog第一个event是描述性event,描述当前文件的格式版本,最后一个event是一个log-rotation event用来指定下一个binary log的文件名。中间的则为常规event,描述各种操作,从event类型上就能比较直观看出event内容,如:XID_EVENT、WRITE_ROWS_EVENT、UPDATE_ROWS_EVENT、DELETE_ROWS_EVENT等。

一个示例如下:

这是GTID模式下,建表和插入数据等events示例。第一个是固定的当前文件头信息,第二个Previous_gtids说明是GTID日志模式,接下来的Gtid event是GTID模式下每个事务的头信息,通过这个不重复且递增的GTID号来保证数据的连续性,一致性,然后是建表并插入数据相关event。

异步复制模式的不足

上面可以看到,binlog中非常清析的记录了所有动作,正常情况下,slave机只要按这顺序执行完成,就能保证主从数据一致。但事务当提交成功后才发日志给slave机,当master出现故障时,slave机收到的日志不一定是完整的,这时没办法完全保证主从数据完全一致,这是异步模式天生的不足,是否有好的解决办法本文不深入,本文要讨论的是当此类故障出现时如何保证外部业务可用的问题。

假如master机掉电了或crash了或操作系统崩溃了,无法恢复使用,此时怎么恢复?先看如下简图:

当master出现问题时,业务将不可用,slave机接收不到binlog,IO线程会处于连接中,HA控制中心确认状态后,会自动把应用流量切到slave机,恢复业务。但是有些情况下,HA控制中心是没办法自我恢复的。如:master在commit之后,开始给slave机传输binlog时,但又没传完,此时master 出现故障,HA需要切换流量到slave,但在切换流量之前,会先等slave机上已经读到的binlog回放完毕。问题就在这里,请看我上面第二节基于binlog的复制简介中加粗字体event为单位事务为单位,在slave机有Exec_Master_Log_Pos记录当前已经执行完成的以事务为单位的日志位置,有Read_Master_Log_Pos记录前已经拉取过来的以event为单位日志位置,当Read_Master_Log_Pos读位置与Exec_Master_Log_Pos执行位置相等就说明slave机上日志已经回放完毕,如果IO线程拉取一个事务的部分event,此时master出现故障不能恢复,slave机会一直等待,此时Read_Master_Log_Pos读位置与Exec_Master_Log_Pos执行位置不一致,HA中心会认为还有日志没回放完,一直等待,正是这个原因造成了本文开头的HA切换不成功故障

HA为什么要等Read_Master_Log_Pos与Exec_Master_Log_Pos一致?虽然异步模式不能完全保证数据不丢失,但要尽量减小丢失。在master并发很大的场景下,主从数据延迟可能会是几十分钟甚至更久,必须要把已经拉取日志回放完毕,减少数据丢失。当出现拉取到不完整事务时,对slave来说是正常状态,可能是网络或其它原因,尝试恢复拉取即可,如果和master通讯恢复正常,slave机是能正常拉取到完整事务的,因此不完整事务状态对slave机说是正常状态。如果master已经不可用,slave机又没拉取完全部事务,Read_Master_Log_Pos与Exec_Master_Log_Pos不一致,HA中心认为回放不完整,不能切换,一直等待。此时甚至DBA上去查看也不好确认slave机状态,如slave机正在回放一个超大事务,需要很长时间,期间master出现故障不可用,此时Read_Master_Log_Pos与Exec_Master_Log_Pos不一致,所有状态在回放过程中不发生改变,无法确定slave机真实状态。

那么当master不可用了,slave机又没拉取完整事务时怎么办?人工检查slave机状态,对比Read_Master_Log_Pos与Exec_Master_Log_Pos,等待一段时间再比较,如果多次比较Exec_Master_Log_Pos没有变化,并且与Read_Master_Log_Pos相差并不大,可以认为已经读取到的日志已经回放完毕,可以把流量切换过来。实际上这不是非常稳妥的方案,操作也需要人工持续观察来做出判断再处理,步骤繁琐,维护成本高,对于云服务厂商,十万甚至百万级别的实例,单点偶尔发小概率事件可能会变成易现大概率事件,需要有更好的处理手段。

Read_Master_Log_Pos更新时机探讨

出现Exec_Master_Log_Pos和Read_Master_Log_Pos不一致的根本原因就在于两个更新时机不一样,一个事务回放结束才更新,一个拉取到event即更新。对于slave机来说,Exec_Master_Log_Pos要等一个事务的全部event拉取过来并且被sql线程回放成功才能更新,但Read_Master_Log_Pos只要有binlog的event被IO线程拉取过来就会改变。而sql回放线程是根据event中标记事务结束或开始的状态对之前事务进行回放,Read_Master_Log_Pos 记录的是当前已经拉取过来的位置,并不影响回放线程。因此可以把Read_Master_Log_Pos更新时机调整到读到完整事务之后再更新,与Exec_Master_Log_Pos更新逻辑保持一致,这样Read_Master_Log_Pos标记的位置就是肯定可以回放的完整事务的位置,不会出现之前那样不完整事务位置造成无法判断真实状态的情况。两种更新方式对比见下图:

当以事务为单位更新Read_Master_Log_Pos 后,无论master什么时机点crash,slave机上Exec_Master_Log_Pos最终都能追上Read_Master_Log_Pos。对于已经被slave机拉取过来的最后一个不完整事务的event,处理逻辑也原来一致,不会继续执行。但HA控制中心已经可以根据各个状态自动做出正确处理的,把流量切入从机,判断逻辑也与原来一样,Read_Master_Log_Pos 与Exec_Master_Log_Pos保持相等即可切换,因为现在任何情况下都成达成这个条件。

修改Read_Master_Log_Pos更新位置注意点

原先逻辑,slave机IO线程并不需要过多解析event,基本上只要确定长度就可,具体内容在sql线程回放时再解析。现在则要在IO线程中确定每个event类型,判断是否是事务结束的event,需要对event做一些解析工作。要修改的点并不多,注意GTID和非GTID区别,考虑row模式和statement差异,对不同情况下事务开始和结束标记理清,在queue_event函数增加处理逻辑即可。

全部评论

相关推荐

头像
10-13 18:10
已编辑
东南大学 C++
。收拾收拾心情下一家吧————————————————10.12更新上面不知道怎么的,每次在手机上编辑都会只有最后一行才会显示。原本不想写凉经的,太伤感情了,但过了一天想了想,凉经的拿起来好好整理,就像象棋一样,你进步最快的时候不是你赢棋的时候,而是在输棋的时候。那废话不多说,就做个复盘吧。一面:1,经典自我介绍2,项目盘问,没啥好说的,感觉问的不是很多3,八股问的比较奇怪,他会深挖性地问一些,比如,我知道MMU,那你知不知道QMMU(记得是这个,总之就是MMU前面加一个字母)4,知不知道slab内存分配器-&gt;这个我清楚5,知不知道排序算法,排序算法一般怎么用6,写一道力扣的,最长回文子串反问:1,工作内容2,工作强度3,关于友商的问题-&gt;后面这个问题问HR去了,和中兴有关,数通这个行业和友商相关的不要提,这个行业和别的行业不同,别的行业干同一行的都是竞争关系,数通这个行业的不同企业的关系比较微妙。特别细节的问题我确实不知道,但一面没挂我。接下来是我被挂的二面,先说说我挂在哪里,技术性问题我应该没啥问题,主要是一些解决问题思路上的回答,一方面是这方面我准备的不多,另一方面是这个面试写的是“专业面试二面”,但是感觉问的问题都是一些主管面/综合面才会问的问题,就是不问技术问方法论。我以前形成的思维定式就是专业面会就是会,不会就直说不会,但事实上如果问到方法论性质的问题的话得扯一下皮,不能按照上面这个模式。刚到位置上就看到面试官叹了一口气,有一些不详的预感。我是下午1点45左右面的。1,经典自我介绍2,你是怎么完成这个项目的,分成几个步骤。我大致说了一下。你有没有觉得你的步骤里面缺了一些什么,(这里已经在引导我往他想的那个方向走了),比如你一个人的能力永远是不够的,,,我们平时会有一些组内的会议来沟通我们的所思所想。。。。3,你在项目中遇到的最困难的地方在什么方面4,说一下你知道的TCP/IP协议网络模型中的网络层有关的协议......5,接着4问,你觉得现在的socket有什么样的缺点,有什么样的优化方向?6,中间手撕了一道很简单的快慢指针的问题。大概是在链表的倒数第N个位置插入一个节点。————————————————————————————————————10.13晚更新补充一下一面说的一些奇怪的概念:1,提到了RPC2,提到了fu(第四声)拷贝,我当时说我只知道零拷贝,知道mmap,然后他说mmap是其中的一种方式,然后他问我知不知道DPDK,我说不知道,他说这个是一个高性能的拷贝方式3,MMU这个前面加了一个什么字母我这里没记,别问我了4,后面还提到了LTU,VFIO,孩子真的不会。
走呀走:华子二面可能会有场景题的,是有些开放性的问题了
点赞 评论 收藏
分享
迷茫的大四🐶:💐孝子启动失败,改为启动咏鹅
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务