【问题描述】

开发有天碰到一个很奇怪的问题,他的场景是这样子的:

通过Canal来订阅MySQL的binlog, 当捕获到有数据变化时,回到数据库,反查该数据的明细,然后做进一步处理。

有一次,他碰到一个诡异的现象:

1.  Canal收到消息,有一条主键id=31019319的数据插入
2. 11:19:51.081, 应用程序去反查数据库,11:19:51.084查询完毕,发现id=31019319的数据为空
3. 过几分钟后,开发去手工查数据库,发现id=31010319的数据是存在的,每次插入的时候,我们会在数据库记录插入时间,发现插入的时间是11:19:51.059。

让开发感到困惑的是11:19:51.059写入的数据,11:19:51.081去查询,应该是能查到数据的呀。我们首先排除了读写分离,主从分离等场景,Canal订阅和数据库查询都是在Master上,所以这个问题就变得非常诡异了。

【问题分析】

因为中间夹杂着Canal, 而Canal是通过binlog读取的,这个问题我们可以简化为:当我们在master插入一条数据,该数据在master还没落库,但是在Slave却能查到。我们尝试重现这种场景。因为我们是采用GTID模式,GTID也就是全局事务编号,我们通过跟踪GTID来调试问题。

我们创建一个测试表如下:

CREATE TABLE `gtid_debug` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

此时,在Master和Slave上,分别收集到的GTID信息如下:

角色 @@global_gtid_executed @@port
Master be7945f1-3613-11ec-8353-98039ba5775a:1-16 3306
Slave be7945f1-3613-11ec-8353-98039ba5775a:1-16 3307

我们在Master上开启gdb调试,在函数ReplSemiSyncMaster::commitTrx上设置断点。

步骤1:

在Master上,开启Session1, 插入一条数据:

insert into gtid_debug(name)values('test1'); 

此时会hit到断点。

步骤2:

在Slave上,开启Session2, 查看GTID:

角色 @@global_gtid_executed @@port
Slave be7945f1-3613-11ec-8353-98039ba5775a:1-17 3307

也就是说,事务在Slave上,开始走字了。

我们进行如下查询:可以看到,在Slave这条记录能被查询到。

slave>select * from test.gtid_debug;
| ID | NAME |
| ---- | ----- |
| 1 | test1 |

步骤3:

在Master上,我们开启Session3, 查看GTID, 这个session也会被断点中断,我们继续执行下一步,直到查询结果返回。注意,此时Session1还停留在断点上,未提交成功。

角色 @@global_gtid_executed @@port
Master be7945f1-3613-11ec-8353-98039ba5775a:1-16 3306

并进行如下查询,返回结果为空:

master>select * from test.gtid_debug;
Empty set

所以我们重现了问题,也就是说,在Master插入数据,事务还没有提交,但在Slave就能查到了。 Slave跑的比Master还快。

【原因分析】

重现了问题后,我们对问题进行分析,并查看了相应代码,发现是半同步复制的模式导致,半同步复制有两种模式: After_Sync(5.7版本默认)模式和After_Commit(5.6版本默认)模式。我们线上的版本是5.7,所以采用的是After_Sync模式。

从上图可以看到,一个事务在半同步模式下提交,无论是after_sync还是after_commit,都要经历4个阶段:

1. InnoDB Redo File Prepare Write
2. Binlog File Flush & Sync
3. InnoDB Redo File Commit (同时释放事务持有的锁)
4. Send binlog to Slave

After_Commit模式的四个阶段顺序为: 1->2->3->4, 而after_sync模式的顺序为1->2->4->3.

在5.7默认的after_sync模式下,确实存在先发送binlog到Slave, 然后再进行事务提交的场景。这时候大家会问了,为啥5.7把半同步复制改为after_sync模式了?这主要是因为after_commit机制存在数据丢失的风险。我们可以设想一下,在3->4的T1时间段,新数据对其它Session已经可见,突然Master挂了,MySQL进行主从切换,这时事务在Master上完成,如在Slave上不存在,切换后,业务会发现之前能查到的数据又没了。

而在after_sync模式下,其执行的顺序为1->2->4->3. 也就是说Master在收到Slave的应答之后,才Commit事务。在3->4的T1时间段内,因事务还未Commit,新数据对其它Session还不可见,所以看上去像比Slave跑的更慢。具体可以参考网上关于这两种模式的讨论。

【解决建议】

我们分析清楚问题之后,解决的方法就比较简单了。不建议改为after_commit模式,虽然改为after_commit模式,可以保证事务在Master落地后,Canal才会读到消息,但存在主从切换事务丢失的风险。我们的解决方法,是在Canal消息处理时,延后1秒再处理。这样解决方法比较合理。因为一般来讲,业务对消息的实时性不是特别高。

MySQL事务还没提交,Canal就能读到消息了?的更多相关文章

  1. 字节一面:事务还没提交的时候,redolog 能不能被持久化到磁盘呢?

    又是被自己菜醒的一天,总结面经看到这题目听都没听过,打开百度就像吃饭一样自然 老规矩,背诵版在文末.点击阅读原文可以直达我收录整理的各大厂面试真题 首先,咱需要明白的是,啥是持久化? 听起来高大上,换 ...

  2. git删除已经提交的包含敏感信息的文件(还没提交到远程仓库)

    写好的代码已经提交了(但还没push到github),发现某个文件里包含密码.如果push的话,密码可就被公开了.如果在代码里改掉密码,再commit一次,也不行,历史提交记录还是会上传到github ...

  3. mysql事务隔离级别、脏读、幻读

    Mysql事务隔离级别本身很重要,再加上可能是因为各大公司面试必问的缘故,在博客中出现的概率非常高,但不幸的是,中国的技术博客要么是转载,要么是照抄,质量参差不齐,好多结论都是错的,对于心怀好奇之心想 ...

  4. <scrapy爬虫>爬取360妹子图存入mysql(mongoDB还没学会,学会后加上去)

    1.创建scrapy项目 dos窗口输入: scrapy startproject images360 cd images360 2.编写item.py文件(相当于编写模板,需要爬取的数据在这里定义) ...

  5. Mysql事务,并发问题,锁机制

    .什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约束 ...

  6. Mysql事务,并发问题,锁机制-- 幻读、不可重复读(转)

    1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约 ...

  7. 【转】Mysql事务,并发问题,锁机制

    转自:http://www.cnblogs.com/fidelQuan/p/4549068.html 1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成 ...

  8. MySQL事务、并发问题、锁机制

    MySQL事务,并发问题,锁机制 1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库 ...

  9. 面试官问你:MYSQL事务和隔离级别,该如何回答

    一.事务 事务是由一组SQL语句组成的逻辑处理单元,是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚.事务具有以下4个属性,通常简称为事务 ...

  10. Mysql事务,并发问题,锁机制-- 幻读、不可重复读--专题

    1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约 ...

随机推荐

  1. Ubuntu linux下gcc、g++不同版本的安装和切换

    讲解linux下gcc.g++不同版本的安装和切换 Ubuntu 18.04预装GCC版本为7.3,但有时在编译是需要用的不同gcc版本,下面介绍,如何安装不同的gcc 和g++,并设置根据不同的需要 ...

  2. 算法题:消除字符串中全部的b和连续的ac

    最近碰到了一道面试题,虽然不难但是临试没想出好的解法,记录下来以作分享. 题目:消除字符串中全部的b和连续的ac 用例: 'aabbc' -> 'a' 'aaabbbccc' -> '' ...

  3. mysql 参数配置

    https://www.jb51.net/article/48082.htm https://www.cnblogs.com/angryprogrammer/p/6667741.html

  4. BGP反射器

    路由反射器是一种减少自治系统内IBGP对等体连接数量的方法. 根据BGP路由通告原则,要求一个AS内的所有BGP Speaker将建立全连接关系(BGP Speaker两两建立邻接关系).当AS内的B ...

  5. 2003031118—李伟—Python数据分析五一假期作业—MySQL的安装以及使用

            项目  期中试卷 课程班级博客链接 20级数据班(本) 这个作业要求链接 作业要求 博客名称 2003031118-李伟-Python数据分析五一假期作业-MySQL的安装以及使用 要 ...

  6. C语言中链表与队列

    https://www.cnblogs.com/lanhaicode/p/10432004.html

  7. 05.常用 API 第二部分

    一.Object 类 是类层次结构的根 (父) 类. String  toString () 返回该对象的字符串表示,其实该字符串内容就是对象的类型 + @ + 内存地址值. 由于 toString ...

  8. (五).JavaScript的数组

    1. 数组 1.1 数组的基础 数组:同种或不同数据类型数据的有序集合 功能:同时存储多个数据 数据:常量 变量 表达式 数组 函数 对象 定义方式:字面量定义或者构造函数定义 字面量定义数组(本质上 ...

  9. Java中的方法增强

    A:在不影响业务情况下,增强一个方法有几种方法呢? B:3种! A:哪三种呀? 一.继承类来重写方法: 1.要可以获取这个类的构造: class Man{ public void run(){ Sys ...

  10. 关于vmwork中centos7的虚拟机创建

    1.准备vmwork软件和centos7的镜像文件 vmwork软件下载地址https://www.vmware.com/cn/products/workstation-pro/workstation ...