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. .NET Core如何进行请求转发?

    前言 冒个泡,近日,有关注我公众号的小伙伴私信我,遇到一个问题搞了很久没解决,此问题具有参考意义,这里跟大家分享下,希望对后续可能有需要的你能有所参考和帮助. 请求转发问题 内网环境跟外网隔离,现在外 ...

  2. 痞子衡嵌入式:RT-UFL - 一个适用全平台i.MXRT的超级下载算法设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天给大家带来的是痞子衡的开源项目 RT-UFL. 痞子衡在近两年多的i.MXRT客户项目支持过程中,遇到的一个相当高频的问题就是制作i.MXRT下载算法.我们 ...

  3. layui导航

    关于导航 首先看一下官网的样式: <!DOCTYPE html><html><head> <meta charset="utf-8" /& ...

  4. 配置PoE交换机功能

    组网图形 PoE简介 随着网络中IP电话.网络视频监控以及无线以太网应用的日益广泛,通过以太网本身提供电力支持的要求也越来越迫切.多数情况下,接入点设备需要直流供电,而接入点设备通常安装在距离地面比较 ...

  5. Activit的心路历程:获取当前节点的下一节点【可能存在多个】的nodeId

    上一任务节点 在我的开发任务中,突然给我提出了一个待办任务需要获取当前任务节点下一任务节点的表单信息,刚开始搞得我有点措手不及,后来仔细是靠后,灵感一下,直接操作流程的bpmn信息就可以获取到节点信息 ...

  6. 手把手教你使用Vuex(二)

    在上一篇文章Vuex(一)中我们已经把Vuex需要用到的属性的单独页面引入到了store/index.js里面,所以我们接下来直接在这些js文件中写自己需要的代码就好. 1.Getter 了解:Get ...

  7. [开源地址] 放弃Flink,.NET5.0开发CSharpFlink,简要设计、部署及二次开发说明。

    github地址:https://github.com/wxzz/CSharpFlinkgitee地址:https://gitee.com/wxzz/CSharpFlink 1 概述及背景 我们有一个 ...

  8. 二:Redis:(REmote DIctionary Server)远程字典服务器

    Redis是完全开源免费的,用C语言编写的,遵循BSD协议,是一个高性能的(key-value)分布式内存数据库,基于内存运行,并支持持久化的NOSQL数据库,是当前最热门的NOSQL数据库之一,也被 ...

  9. CSS属性(边框)

    1.边框 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="U ...

  10. 用JavaScript做精灵图

    用JavaScript做精灵图 精灵图可以不用在给每一个小块一 一的修改位置.主要原理是找到整张的背景图与li的下标的数学关系. 这是一大张背景图,这个背景图的位置其实是有规律的,每两张之间间隔一个固 ...