Hi,大家好!我是白日梦。

今天我要跟你分享的话题是:“MySQL是如何根据undo log 链条实现read view机制的?谈谈看”

一、事物的隔离级别与MVCC?

MySQL单进程多线程的数据库软件,在事务的并发操作中可能会出现脏读,不可重复读,幻读。

MySQL支持的四种事务隔离级别如下:

  • Read uncommited

    简单来说就是:事务A可以读到事务B未commit的数据。这种情况也被叫做脏读。

  • Read commited

    简单来说就是:事务A可以读到事务B已经commit的数据。

  • Serializable

    在该级别下,写会加写锁、读会加读锁,除了读读不互斥,其他组合都互斥,因此可以保证事务串行化顺序执行,可以避免脏读、不可重复读与幻读。

  • Repeatable read

    如下图:可重复读要求事物A两次 select 查询出来的结果是一样的,即使中间事物B将id=1的行给修改了,也要保证事物A再读取时,读到的结果也得和第一次读到的结果相同。

但是可重复读存在幻读读问题,比如事物A开启后按某个范围X读取一次(事物未提交),这时其他事物在该范围X内插入了新的数据,事物A再读时就会将新插入的数据读取出来,当然在MySQL的RR隔离级别下不会再出现这种幻行的问题。

问题的解决得益于:MVCC多版本并发控制的快照读和next-key lock 当前读。

二、Repeatable Read是如何实现的

以RR隔离级别为例:

你可以像下面这样看一下你的MySQL默认使用的什么隔离级别:

MVCC多版本并发控制也被称为快照读,在RR的隔离级别下,当事物开启时会创建一个视图(Read View),其实这个视图就是所谓的快照。在整个事物存在的期间,一直会使用这个视图。

下面看一个九个步骤的小实验:

上图中的右部分的会话中begin之后,就会创建读视图,所以它的多次select使用的是同一个视图,所以结果都是一样的。即使数据中途被左边的事物更改了,它也没有受到影响。

再结合视图去理解这个过程。

当你执行begin开启事物之后,MySQL会拍下像下图这样的快照:

上图中的trx_ids中记录着MySQL中活跃的且未提交的事物。

假设有事物A、事物B擦不多在同一时刻开启,那这两个事物会分别得到如下的视图。

在RR的隔离级别下,事物一开启就会得到上图那样的ReadView,并且只要事物不提交这个ReadView就一直有效。

就上图来说:

在事物A的视图中,它的事物ID=61,此时活跃的事物集合是[61、62],活跃的事物ID中最小的事物id是它本身。下一个事物id应该是63。

在事物B的视图中,它的事物ID=621,此时活跃的事物集合是[61、62],活跃的事物ID中最小的事物id是61。下一个事物id应该是63。

先让事物A尝试去读取name列的数据。

它会发现的这行数据的Data_TRX_ID=60,通过和trx_ids对比发现这个事物ID不在活跃的事物id集合trx_ids中,并且小于它本身的60。说明:在事物A开启之前,事物ID=60的事物早就提交过了。所以事物A能直接这行数据name = tom。

然后事物B通过update语句尝试去修改这行数据,想将name 改成 jetty。这时MySQL会记录相应的undo log,并以链表的方式串联起来,于是我们会得到下图:

你可以看到上图中,由于事物B将name改成jerry,导致多出一条undo log。这条undo对应的事物ID=事物B的事物ID = 62。并且通过一个指针执向它的上一个undo log记录。

这时如果事物A重新去读,首先它会读取到的记录是name = jerry,但是它也会发现该记录的trx_id = 62 , 比自己的61还大,并且比下一个事物ID63小。说明:它读到记录其实是和自己同时开启的事物修改后的产物,这时他就会沿着undo log链条往前找,直到找到第一个trx_id等于或者小于自己事物ID的记录为止。所以事物A再一次读取到trx_id = 60的记录。

这也就是所谓的快照读机制。

另外需要注意的是:就上例来说,在RR的隔离级别下,确实能保证事物A每次读取出来的结果都是一样的,而且在事物B将其修改后,事物A依然能读取出name = tom。但是这时name=tom真的只是个快照,本质上它已经可以算是不存在是数据了。

本文是MySQL专题第15篇,全文近100篇(公众号首发)

本文是第15篇,全文近100篇,点击查看目录

三、Read Commited是如何实现的:

在RR隔离级别下,当事物一开始视图就会被创建出来,并且一直到该事物提交该视图都有效。

在Read Commited隔离级别,每次select 都会创建一个新的视图。

还是使用这个例子:假设事物A和事物B并发开启,并且各自得到了图中的ReadView。然后很快,事物B就将数据name = tom改成了name = jerry(未提交)。那这时事物A去select会检索出什么结果呢?

事物A检索过程:事物A首先会沿着undo log链条从头开始找,于是它首先找到name = jerry的列。但是它也发现该列的trx_id = 62 不但比自己的事物ID60大,而且还在trx_ids这个活跃事物列表中,说明name = jerry是被和自己差不多同时开启的其他事物更改的。它自然也就读不到。

紧接着事物B提交事物,然后事物A重新select会开启一个新的视图,得到如下图:

当事物A沿着undo log链条往下查找时,他发现首先发现的name = jerry的行的trx_id是62,竟然比自己的事物ID61还大,但是进一步发现,这个事务ID62并不在trx_ids中。说明,这个其实是已经被提交了的数据,那直接就意味着其实自己是允许读出这条数据的。这也就是所谓的读已提交机制。

### 四、长事物的风险

其实文章看到这里,长事物有什么风险你应该也可以感觉出来了。

事务迟迟不结束,就意味着它随时可能会访问到数据库中任何数据,所以只要是它们可能用的回滚记录,数据库都得为他们保留着。所以事物越长,相应的他对应的视图也就越大。

上一篇文章中白日梦有和大家介绍过 undo log 默认存放在共享表空间文件中,同时在SQL5.6 MySQL5.7在也允许你将undo log拿到单独的表空间中去,但是不论怎样,undo log总会以真实存在的文件的形式存在于磁盘上,当然了MySQL5.7的undo truncate机制 结合purge线程可以将不需要的undo log清除掉,为undo log文件瘦身,但是在这之前undo log的体量会不断的增大,再加上大量的长事物,很可能会将磁盘打爆。

另外长事物大概率是update等DML导致的,这种DML是会持有行锁的。谁也不能保证长时间不释放锁不会导致数据库被拖垮。

本文是MySQL专题第15篇,全文近100篇(公众号首发)

本文是第15篇,全文近100篇,点击查看目录

我劝!这位年轻人不讲MVCC,耗子尾汁!的更多相关文章

  1. Python爬取B站耗子尾汁、不讲武德出处的视频弹幕

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 前言 耗子喂汁是什么意思什么梗呢?可能很多人不知道,这个梗是出自马保国,经常上网的人可能听说过这个 ...

  2. JLC PCB 嘉立创自动确认生产稿,不讲武德?耗子尾汁!!!

    首先,开局一张图,嘉立创又不做人的一天.嘉立创不讲武德,耗子尾汁!!! 之前下单,勾选了确定生产稿和不加客编,结果生产稿出来还是给我加了客编.那我出10元的意思何在?让我自己花3元看我花的10元有没有 ...

  3. CommandLineRunner 可能会导致你的应用宕机停止,我劝你耗子尾汁

    hello,大家好,我是小黑,又和大家见面啦~~ 如果你去某度搜索关键词 CommandLineRunner 初始化资源 ,截止小黑同学写这篇推文之前,大概能收到 1,030,000 个结果. 网上大 ...

  4. 新同事不讲“码”德,这SQL写得太野了,请耗子尾汁~

    今天来分享几个MySQL常见的SQL错误(不当)用法.我们在作为一个初学者时,很有可能自己在写SQL时也没有注意到这些问题,导致写出来的SQL语句效率低下,所以我们也可以自省自检一下. 1. LIMI ...

  5. 年轻人不讲武德来白piao我这个老同志

    朋友们好啊,我是码农小胖哥. 今天有个同学问我在吗,我说什么事? 给我发个截图,我一看!噢,原来是帮忙搞个定时任务,还是动态的. 他说了两种选择,一种是用DelayQueue,一种是用消息队列. 他说 ...

  6. 年轻人不讲武德,竟然重构出这么优雅后台 API 接口

    Hello,早上好,我是楼下小黑哥~ 最近偶然间在看到 Spring 官方文档的时候,新学到一个注解 @ControllerAdvice,并且成功使用这个注解重构我们项目的对外 API 接口,去除繁琐 ...

  7. 谈谈传说中的redo log是什么?有啥用?

    目录 一.引出 redo log 的作用 二.思考一个问题: 三.redo log block 四.redo log buffer 五.redo log的刷盘时机 六.推荐参数 七.redo log ...

  8. 一起看下MySQL的崩溃恢复到底是怎么回事

    目录 回顾 思考一个问题 checkponit机制 Checkpoint的种类及触发条件 LSN 推荐阅读 本文稍微有点晦涩.但是看过之后你就能Get到MySQL的崩溃恢复到底是怎么做的! 文章公号 ...

  9. MySQL的binlog有啥用?谁写的?在哪里?怎么配置

    目录 一.唠嗑 二.什么是bin log? 三.它在哪里? 四.bin log的相关配置 五.binlog 有啥用? 六.超有用的参数 sql_log_bin 七.未来几篇文章 推荐阅读 一.唠嗑 文 ...

随机推荐

  1. python实现类的多态

    多态 关注公众号"轻松学编程"了解更多. 1.多态使用 一种事物的多种体现形式,举例:动物有很多种 注意: 继承是多态的前提 函数重写就是多态的体现形式 演示:重写Animal类 ...

  2. 如何k个一组反转链表

    之前的文章「递归反转链表的一部分」讲了如何递归地反转一部分链表,有读者就问如何迭代地反转链表,这篇文章解决的问题也需要反转链表的函数,我们不妨就用迭代方式来解决. 本文要解决「K 个一组反转链表」,不 ...

  3. How to refresh datasource args caller[X++]

    To refresh  datasource args caller, you must add override method close on form like source code belo ...

  4. ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)

    在我们开发业务的时候,一般数据库表都有相关的关系,除了单独表外,一般还包括一对多.多对多等常见的关系,在实际开发过程中,需要结合系统框架做对应的处理,本篇随笔介绍基于ABP框架对EF实体.DTO关系的 ...

  5. 【QT】QtConcurrent::run()+QThreadPool实现多线程

    往期链接: <QThread源码浅析> <子类化QThread实现多线程> <子类化QObject+moveToThread实现多线程> <继承QRunnab ...

  6. 关于ubuntu出现的一些问题的解决方法

    1. (1)现象: dpkg: 处理软件包 linux-image-4.15.0-36-generic (--configure)时出错: 子进程 已安装 post-installation 脚本 返 ...

  7. python之 栈与队列

    忍不住想报一句粗口"卧槽"这尼玛python的数据结构也太特么方便了吧 想到当初学c语言的数据结构的时候,真的是一笔一划都要自己写出来,这python尼玛直接一个模块就ok 真的是 ...

  8. python之路《八》装饰器

    装饰器是个好东西啊 那么装饰器是个什么样的东西呢,他又能做些什么呢? 1.为什么装饰器 当我们一个程序已经构建完成,并且已经发布出去了,但是现在需要增加一个活动,例如淘宝给你发送一个今日优惠,或者开启 ...

  9. 01 Servlet技术

    Servlet 1.Servlet技术 1.1.什么是Servlet Servlet是JavaEE规范之一.规范就是接口 Servlet就JavaWeb三大组件之一.三大组件分别是:Servlet程序 ...

  10. mongo命令行操作