转自:http://blog.chinaunix.net/uid-20639775-id-4664792.html#_Toc29623

ROW模式的SQL无法正常同步的问题总结

一、 问题起因................................................................................................................. 2

二、 排查过程................................................................................................................. 3

三、 深入分析................................................................................................................. 4

四、 问题解决................................................................................................................. 6

五、 问题跟进处理.......................................................................................................... 6

六、 参考资料................................................................................................................. 7

 

最近处理数据库问题时遇到一起mysql从机ROW模式的SQL无法正常同步的问题,今天刚好有时间,将整个过程总结一下,方便后面的同学学习!

一、问题起因

最近有一个业务的实例在比对数据的时候数据库、表、以及行数都是一样的,只是有多个表的checksum值不一致,主从状态也正常。初步判断可能是运维之前有做过skip的操作导致,对从机进行了重做后发现问题依旧,于是对binlog的内容进行了分析跟进,来找到不能同步的根本原因。分析binlog的过程如下:

?         选择一条checksum主从不一致的表的一条最近的记录

从上面两张图中可以很清楚的看出两条记录的内容不一致,那么为什么会不一致呢?我们来追踪binlog看看

?         通过binlog进行追踪master上这条记录的执行情况

在master上找到了对应的update的记录。下面到从机上找一下对应的relay日志和binlog看看是否有正常复制和执行。

?         在slave的relay日志和binlog中都查找对应master上的那条binlog的执行情况

在slave上发现一个奇怪的现象,在relay日志中能找到对应的更新这条语句的SQL,也就是说在master上的binlog已经通过从机的IO线程将对应的update语句同步到了relay日志中。但是在slave的binlog中没有找到运行这条SQL的记录,并且从机上的位置早就已经超过了那个update的位置,排除了slave端延迟的问题。

跟踪多条ROW模式的SQL均是此问题,而STATEMENT的SQL不会出现异常的问题,从机上的所有的binlog显示都是STATEMENT的SQL。主从binlog的比较也能看出确实有异常(备注:开启了log-slave-updates参数),如下截图所示:

Master上的binlog的数量和大小:

Slave上的binlog的数量和大小:

汇总问题如下:

Slave无法同步Master上的ROW模式的SQL

二、排查过程

出现这个奇怪的灵异问题,首先想到的是mysql某个版本的bug,找各个版本的服务器进行重现该问题,发现相同的SQL在其他服务器都没有此问题。但是当采用其他的各个版本或者相同版本的mysql作为那台有问题的master的从机,就都出现了不能同步ROW模式的SQL的问题。因此基本可以排除版本的问题。

那么是什么原因在从机上ROW模式的SQL没有执行呢?什么场景会触发这种问题呢?

经过开发同学zhiyangli和edgeyang的源码定位终于找到了问题原因,问题原因是sql线程将relay log保存的64位table id转换成32位溢出,导致在hash结构中找不到对应的表而不进行任何操作,具体的逻辑为:binlog中table_id是一个ulong类型(无符号长整形),在slave进行重做binlog events之前,会先将这个ulong的table_id(为了避免混淆,用m_table_id表示)传给一个它内部维护的一个数据结构RPL_TABLE_LIST,这个里面有一个变量table_id用来存储binlog中的m_table_id,问题出现了:数据结构的变量table_id是一个uint(无符号整形),如果m_table_id超过uint的范围会发生截断。而MySQL内部在构造hash,从hash表中取值是这样的做法:set_table(table_id),get_table(m_table_id),在两个阶段用到的key因为发生了数据截断所以必然也就不能取到预期的值。也就是说之前用uint型的table_id构建出来的key-value的hash对,用ulong型的m_table_id是无法查询到的。也就是如果binlog中显示的tableid超过2的32次方就是42亿的时候就会触发这个bug。通过重启能临时解决问题。如下截图就是有问题的master产生的binlog:

从这个图中我们可以看出table id已经远远超过了42亿了,达到了109亿。

三、深入分析

既然是由于tableid导致,那么tableid是什么东西,为什么要有tableid这个东西呢,以及为什么STATEMENT模式的就不需要tableid呢?另外为什么tableid为上涨得那么厉害超过42亿?带着这些问题,下面就来慢慢分析和解答:

在引入table id之前我们先来说一下mysql的binlog格式

(一)Mysql的binlog格式

搞mysql的同学都知道,mysql的binlog分为三种格式,一种是STATEMENT格式,一种是ROW格式,最后一种是结合STATEMENT和ROW的MIXED格式。下面比对一下各个格式的优缺点:

1.         STATEMENT

a)         优点

只记录执行的SQL语句本身,binlog量少,节省IO,性能比较好

b)        缺点

对一些却确定的函数比如uuid()、limit、user()等不能保证主从数据的一致性。

2.         ROW

a)         优点

Row格式非常清楚地记录下每一行数据的修改细节,能保证主从数据的一致性。

b)        缺点

Binlog太多,IO性能受限制,另外对从机的主从延迟也是一个挑战。

3.         MIXED

结合了STATEMENT和ROW模式的有点。

(二)Table id是个啥东西

先来看两个binlog中的SQL语句

STATEMENT格式的binlog

 

从mysql的binlog中发现statement的SQL是没有table id的,从STATEMENT中记录的SQL,我们可以看出,通过SQL就知道更改表的对应位置,因此不需要通过table id去查找到对应的表的结构信息。

ROW格式的binlog

 

从截图中可以看出ROW模式中含有table id的概念,ROW模式引入table id是为了在执行insert/update/delete解析的时候能够知道具体的表信息,因为我们通过binlog可以看到,语句并不能反应出列名信息。因此通过table id来关联表结构信息。从table_map_id代码中也能看table id就是专门用于ROW格式的:

ulong table_map_id; /* for row-based replication */

(三)Tablemap和table id

ROW模式的binlog中有如下两行信息:

Table id就是table map映射key ID,从binlog中可以看到mysql分2个events分别记录这些信息events 1,记录了操作哪些库哪些表。其中会将这些信息缓存到一个hash map内,key为tabke_id,value为table类(保存了库名,表名等信息),events 2记录了操作哪些行。每次执行events 2的时候,mysql通过table_id先去hash map查找相关的table信息。找到库表后再操作具体的行。一个table map events可以对应多个row events,以此减少binlog占用空间。

(四)Table id增长和cache的关系

从代码中可以看出table id的分配在函数assign_new_table_id(),每次分配都是对上一次的table id自增,代码如下:

一般是DDL语句会导致table id增加。

下来再看看table id和cache的关系,网上有代码分析了,table id是保存在cache中,当cache中有该表定义时,表对应的table id是不变的,而当cache中没有改表定义时,该值根据上一次操作的table id自增1获得的。Cache指的是table cache,由table_definition_cache组成,这里就会引出一个问题,当table cache过小而表的数量又很多的场景,会导致表定义将被频繁置换出cache,被置换出的表如果有操作时,重新加载时,table id的值就会发生改变。因此,table id与实际操作的数据表没有直接对应关系,而与操作的数据表是否在table cache中有关。

总结有如下两个方面会导致table id增加:

?         DDL语句执行的时候。

?         Table cache设置太小,表定义被频繁置换出cache,导致table id增加。

?         执行flush tables

(五)为什么table id超过42亿同步就有问题呢?

这里涉及到mysql的bug,在定义table id的时候采用的ulong型,为8byte。而在同步的SQL线程中设置的table id为uint型,为4byte,因此同步的SQL线程中如果超过2^32的话就溢出了,主机的update等就无法同步更新到从机。具体代码如下:

四、问题解决

知道了问题原因就好解决了,主要有如下两种解决办法:

1.         修改代码修复uint的问题。

2.         重启实例,并将table cache调大。

五、问题跟进处理

对于一个平台来讲,虽然遇到的机会比较少,但是这种问题侧出现反应了我们平台还是有一些监控的盲点和漏洞,需要对table id进行监控,另外对table cache的默认配置400还是非常小的。因此接下来三个任务:

1.更改线上的版本,修复uint的问题。

2.对于存量的需要将table cache进行一次整体的调整。

3.推动添加table id的监控,防止类似的问题出现。

六、参考资料

MySQL Binlog中TABLE ID源码分析

http://blog.chinaunix.net/uid-26896862-id-3329896.html

mysql TableMap id递增问题

http://agapple.iteye.com/blog/1797061

淘宝物流MySQL slave数据丢失详细原因

http://hatemysql.com/2012/11/23/%E6%B7%98%E5%AE%9D%E7%89%A9%E6%B5%81mysql-slave%E6%95%B0%E6%8D%AE%E4%B8%A2%E5%A4%B1%E8%AF%A6%E7%BB%86%E5%8E%9F%E5%9B%A0/

ROW模式的SQL无法正常同步的问题总结的更多相关文章

  1. MySQL在ROW模式下通过binlog提取SQL语句

    Linux基于row模式的binlog,生成DML(insert/update/delete)的rollback语句通过mysqlbinlog -v 解析binlog生成可读的sql文件提取需要处理的 ...

  2. SQL Server AlwaysON 同步模式的疑似陷阱

    原文:SQL Server AlwaysON 同步模式的疑似陷阱 SQL Server 2012 推出的最重要的功能之一Alwayson,是一个集之前Cluster和Mirror于一体的新功能,即解决 ...

  3. SQL SERVER 2005 同步复制技术(转)

    SQL SERVER 2005 同步复制技术 以下实现复制步骤(以快照复制为例) 运行平台SQL SERVER 2005 一.准备工作: 1.建立一个 WINDOWS 用户,设置为管理员权限,并设置密 ...

  4. SQL Server 复制 - 发布订阅(SQL Server 数据同步)

    原文:SQL Server 复制 - 发布订阅(SQL Server 数据同步) SQL Server的同步是通过SQL Server自带的复制工具来实现的,分发布和订阅2大步. A,复制-发布 发布 ...

  5. SQL Server 2005同步复制

    原文:SQL Server 2005同步复制 以下实现复制步骤(以快照复制为例) 运行平台SQL SERVER 2005 一.准备工作: 1.建立一个 WINDOWS 用户,设置为管理员权限,并设置密 ...

  6. 单用户模式启动SQL Server实例总结

      在SQL Server的数据库维护过程中,有时候在一些特殊情况下需要在单用户模式下启动SQL Server实例. 下面总结一下单用户模式启动SQL Server的几种方式: 1:命令模式(sqls ...

  7. SQL Server2005配置同步复制

    因为实际需要,需要对两台SQL Server服务器配置同步复制.记录一下配置过程. 配置环境 两台服务器: 发布服务器,以下简称PS. 订阅服务器,以下简称SS. 两台服务器都是Windows Ser ...

  8. SQL Server 数据库同步,订阅、发布、复制、跨服务器

    随便说两句 折腾了一周,也算把数据库同步弄好了.首先局域网内搭建好,进行各种测试,弄的时候各种问题,弄好以后感觉还是挺简单的.本地测试好了,又在服务器进行测试,主要的难点就是跨网段同步,最后也解决了, ...

  9. MySQL Binlog--基于ROW模式的binlog event大小限制

    参数binlog-row-event-max-size:Specify the maximum size of a row-based binary log event, in bytes. Rows ...

随机推荐

  1. 【数论】【欧拉函数】bzoj2190 [SDOI2008]仪仗队

    由图可知,一个人无法被看到时,当且仅当有 人与原点 的斜率与他相同,且在他之前. ∴一个人可以被看到,设其斜率为y/x,当且仅当y/x不可再约分,即gcd(x,y)=1. 考虑将图按对角线划分开,两部 ...

  2. 【分块】bzoj2724 [Violet 6]蒲公英

    分块,离散化,预处理出: ①前i块中x出现的次数(差分): ②第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现的次数,加上差分的结 ...

  3. 【tarjan求割顶】BZOJ2730-[HNOI2012]矿场搭建

    [题目大意] 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍 ...

  4. Xcode8 不能显示blame,show blame for line 灰色不可点解决办法

    1.原因 创建工程时没勾选create git repository 2.解决办法 ➜  ~  cd /Users/zhanglinfeng/Documents/EastMoney/EMLive  / ...

  5. web 中加载配置文件

    1.web.xml中配置   <!-- 加载配置文件 -->   <listener>      <description>ServletContextListen ...

  6. JS面向对象之作用域

    作用域 词法作用域 作用域 域表示的就是范围,即作用范围 就是一个名字在什么地方能使用,在什么地方不能使用 块级作用域 块级别的作用范围 // 在 c , java 等编程语言中,下面的语法报错 { ...

  7. ActiveX控件在项目中的应用

  8. 14、高可用keepalived搭建及切换

    keepalived主从切换试验: 1.先搭建192.168.1.20与192.168.1.21的主主架构     192.168.1.76为VIP 2.在2机都安装keepalived  tar - ...

  9. 原型设计工具——Axure

    1,百度百科 http://baike.baidu.com/view/3332366.htm?from_id=5056136&type=syn&fromtitle=axure& ...

  10. Java笔记6:多态

    一.多态的分类对象的多态性:动物 x = new 猫();函数的多态性:函数重载.重写 二.多态的体现父类的引用指向了自己的子类对象父类的引用也可以接收自己的对象 三.多态的前提必须是类与类之间只有关 ...