近日接到一个故障,主从异步方式,主 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函数增加处理逻辑即可。

主从复制系列C的更多相关文章

  1. 主从复制系列A

    一.主从原理 Replication 线程   Mysql的 Replication 是一个异步的复制过程,从一个 Mysql instace(我们称之为 Master)复制到另一个 Mysql in ...

  2. 主从复制系列B

    从服务器靠中继日志来接收从主服务器上传回来的日志.并依靠状态文件来记录已经从主服务器接收了哪些日志,已经恢复了哪些日志. 中继日志与二进制日志的格式相同,并且可以用mysqlbinlog读取.SQL线 ...

  3. MySQL 系列(四)主从复制、备份恢复方案生产环境实战

    第一篇:MySQL 系列(一) 生产标准线上环境安装配置案例及棘手问题解决 第二篇:MySQL 系列(二) 你不知道的数据库操作 第三篇:MySQL 系列(三)你不知道的 视图.触发器.存储过程.函数 ...

  4. solr与.net系列课程(七)solr主从复制

    solr与.net系列课程(七)solr主从复制    既然solr是解决大量数据全文索引的方案,由于高并发的问题,我们就要考虑solr的负载均衡了,solr提供非常简单的主从复制的配置方法,那么下面 ...

  5. Redis系列(四):Redis的复制机制(主从复制)

    本篇博客是Redis系列的第4篇,主要讲解下Redis的主从复制机制. 本系列的前3篇可以点击以下链接查看: Redis系列(一):Redis简介及环境安装 Redis系列(二):Redis的5种数据 ...

  6. Mongodb数据库学习系列————(一)Mongodb数据库主从复制的搭建

    Mongodb数据库主从复制的搭建 Writeby:lipeng                                    date:2014-10-22 最近项目上用到了位置查询,在网上 ...

  7. 【FAQ系列】关于SQL_Errno:1677导致主从复制中断的思考和实践

    1.简单介绍该错误发生的背景: 1) 数据库版本:MySQL5.7.19 2) 对一个大表修改字段类型DDL(将主键id int变为bigint),为了不影响主库业务,先在从库上执行DDL操作,然后通 ...

  8. Redis系列八:redis主从复制和哨兵

    一.Redis主从复制 主从复制:主节点负责写数据,从节点负责读数据,主节点定期把数据同步到从节点保证数据的一致性 1. 主从复制的相关操作 a,配置主从复制方式一.新增redis6380.conf, ...

  9. MySQL系列详解六:MySQL主从复制/半同步演示-技术流ken

    前言 随着技术的发展,在实际的生产环境中,由单台MySQL数据库服务器不能满足实际的需求.此时数据库集群就很好的解决了这个问题了.采用MySQL分布式集群,能够搭建一个高并发.负载均衡的集群服务器.在 ...

随机推荐

  1. String str = new String("abc"),这段代码一共生成了几个String对象?为什么?

    String str = new String("abc")创建了俩个对象,首先为创建一个String对象"abc",然后在调用String类的构造方法时 pu ...

  2. [JZOJ6271] 2019.8.4【NOIP提高组A】锻造

    题目 题目大意 武器的每个级别有固定的两种属性\(b_i\)和\(c_i\) 可以用\(a\)的代价得到一把\(0\)级的武器. 可以将\(x\)级武器和\(y=\max(x-1,0)\)级武器融合锻 ...

  3. 思维题+栈的应用——cf1092D有意思

    第一例很简单,把两个差为偶数的列不断合并即可 这种不需要撤销的合并相连数直接用栈来做 /* 如果相邻两列高度差为偶数 那么可以直接消去 */ #include<bits/stdc++.h> ...

  4. docker中国区镜像加速

    [root@syzyy ~]# vim /etc/docker/daemon.json { "registry-mirros":[ "https://registry.d ...

  5. overload和override二者之间的区别

    overload和override三者之间的区别 Overload是重载,是有相同的方法名,但参数类型或个数彼此不同Override是重写,是在子类与父类中,子类中的方法的方法名,参数个数.类型都与父 ...

  6. 【Vue】组件的基础与组件间通信

    转载:https://segmentfault.com/a/1190000016409329 Vue.js 最核心的功能就是组件(Component),从组件的构建.注册到组件间通信,Vue .x 提 ...

  7. 026_JDBC

    JDBC简介 JDBC(Java DataBase Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编 ...

  8. 第四周课堂笔记3th

    1.函数的嵌套 作用域,说的是变量 全局作用域:内置命名空间,全局命名空间  全局空间不可以引用局部空间 局部作用域: 局部命名空间  开辟的临时空间前提是调用了函数 全局作用域在整个文件中被使用 L ...

  9. [AHOI2014/JSOI2014]骑士游戏

    题目 思博贪心题写了一个半小时没救了,我也没看出这是一个\(spfa\)来啊 设\(dp_i\)表示彻底干掉第\(i\)只怪物的最小花费,一个非常显然的事情,就是对于\(k_i\)值最小的怪物满足\( ...

  10. java_缓冲流(文件内容排序)

    /** 案例:诸葛亮出师表文本排序 * 1.使用HashMap集合,k存储每行文本序,v存储文本 * 2.创建字符缓冲输入流,构造方法中绑定字符输入流 * 3.使用字符串缓冲输入流中的方法readLi ...