MySQL 笔记整理(8.a) --事务到底是隔离还是不隔离的?
笔记记录自林晓斌(丁奇)老师的《MySQL实战45讲》
8.a) --事务到底是隔离还是不隔离的?
这部分内容不太容易理解,笔者也是进行了多次阅读。因此引用原文:
之前有提到过,如果是在可重复读隔离级别,事务T启动的时候会创建一个视图read-view,之后事务T执行期间,即使有其他事务修改了数据,事务T看到的仍然跟在启动时看到的一样,也就是说,一个在可重复读隔离级别下执行的事务,好像与世无争,不受外界影响。
但是,在之前也行锁相关内容时又提到,一个事务要更新一行,如果刚好有另外一个事务拥有这一行的行锁,它又不能这么超然了,会被锁住,进入等待状态。问题是,既然进入了等待状态,那么等到事务自己获取到行锁要更新数据的时候,它读到的值又是什么呢?
举一个例子,下面是一个只有两行的表的初始化语句。
mysql> CREATE TABLE `t` ( `id` ) NOT NULL, `k` ) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB; ,),(,);

图一, 事务A, B, C的执行流程
这里我们需要注意的是事务的启动时机。
begin/start transaction命令并不是一个事务的起点,在执行到它们之后的第一个操作InnoDB表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用start transaction with consistent snapshot命令。
- 第一种启动方式,一致性视图是在执行第一个快照读语句时创建的。
- 第二种启动方式,一致性视图是在执行start transaction with consistent snapshot时创建的。
还需要注意,没有特殊说明的情况下,我们默认autocommit=1.
在上面这个例子中,事务C没有显示的使用begin/commit,表示这个update语句本身就是一个事务,语句完成时会自动提交。事务B在更新了行之后查询;事务A在一个只读事务中查询,并且时间顺序上是在事务B的查询之后。这时候,如果告诉你事务B查询到的K值是3,而事务A查到的k的值是1,你是不是感觉有点晕呢?
所以今天这篇文章就是想和你说明这个问题。在MySQL里有两个视图概念:
- 一个是view,它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是create view...,而它的查询方法与表一样。
- 另一个是InnoDB在实现MVCC时用到的一致性视图,即consistent read view,用于支持RC(Read Commited 读提交)和RR(Repeatable Read,可重复读)隔离级别的实现。
它没有物理结构,作用是事务执行期间用来定义“我能看到什么数据”.
“快照”在MVCC里是怎么工作的?
在可重复读的隔离级别下,事务在启动的时候就“拍了个快照”,注意,这个快照是基于整个数据库的。这时,你可能觉得这不太现实,毕竟如果一个库有100G,那么我启动一个事务,MySQL就要拷贝100G的数据,这个过程得多慢啊。可是平常执行事务却很快。实际上,并不需要拷贝这100G的数据。我们先来看看这个快照是怎么实现的。
InnoDB里面每个事务有一个唯一的事务ID,叫做transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。
而且,每行数据也是有多个版本的,每次事务更新的时候,都会生成一个新的数据版本,并且把transaction id 赋值给这个数据版本的事务ID,记为row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。也就是说,数据表中的每一行记录,其实可能有多个版本(row),每个版本有自己的row trx_id。
如图2所示,就是一个记录被多个事务连续更新后的状态。

图2 行状态变更图
图中虚线框里是同一行数据的4个版本,当前最新版本是V4,k的值是22,它是被transaction id为25的事务更新的,因此它的row trx_id也是25.你可能会问,前面的文章不是说,语句更新会生成undo log(回滚日志)吗?那么,undo log在哪儿呢?实际上,图2中的三个虚线箭头,就是undo log;而V1,V2,V3并不是物理上真实存在的,而是每次需要的时候根据当前版本和undo log计算出来的。比如,需要V2的时候,就是通过V4依次执行U3,U2算出来。明白了多版本和row trx_id的概念后,我们再来想一下,InnoDB是怎么定义那个“100G”的快照的。按照可重复读的定义,一个事务启动的时候,能够看见所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。
因此,一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本在我启动之前生成,就认;否则就不认。必须要找到它的上一个版本。当然,如果‘上一个版本’也不可见,那就得继续往前找”。除此之外,如果这个事务自己更新的数据,它自己还是要认的。在实现上,InnoDB为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务ID。“活跃”指的就是,启动了但还没提交。数组里面事务ID的最小值记为低水位,当前系统里面已经创建过的事务ID的最大值加1记为搞水位。这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。而数据版本的可见性规则,就是基于数据的row trx_id和这个一致性视图的对比结果得到的。这个视图数组把所有的 row trx_id分成了几种不同的情况。

图3, 数据版本可见性规则
这样,对于当期事务的启动瞬间来说,一个数据版本的row trx_id,有以下几种可能:
- 如果落在绿色部分,表示这个版本是已提交的事务或者是当期事务自己生成的,这个数据是可见的。
- 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的。
- 如果落在黄色部分,那就包括两种情况。a. 若row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见。b.若 row trx_id不在数组中,表示这个版本是已经提交了的事务生成的,可见。
所以你现在知道了,InnoDB利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。接下来我们回顾一下图1中的三个事务,分析事务A的语句返回的结果,为什么是k=1.
未完待续.....
MySQL 笔记整理(8.a) --事务到底是隔离还是不隔离的?的更多相关文章
- 最全mysql笔记整理
mysql笔记整理 作者:python技术人 博客:https://www.cnblogs.com/lpdeboke Windows服务 -- 启动MySQL net start mysql -- 创 ...
- MySQL 笔记整理(8.b) --事务到底是隔离还是不隔离的?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 8.a) --事务到底是隔离还是不隔离的? 本周工作较忙,加上懒惰,拖更 ...
- MySQL 笔记整理(1) --基础架构,一条SQL查询语句如何执行
最近在学习林晓斌(丁奇)老师的<MySQL实战45讲>,受益匪浅,做一些笔记整理一下,帮助学习.如果有小伙伴感兴趣的话推荐原版课程,很不错. 1) --基础架构,一条SQL查询语句如何执行 ...
- MySQL 笔记整理(3) --事务隔离,为什么你改了我还看不见?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 3) --事务隔离,为什么你改了我还看不见? 简单来说,事务就是要保证一组数据操作,要么全部成功,要么全部失败.在MySQL中,事务 ...
- MySQL 笔记整理(16) --“order by”是怎么工作的?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 16) --“order by”是怎么工作的? 在林老师的课程中,第15 ...
- MySQL 笔记整理(14) --count(*)这么慢,我该怎么办?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 14) --count(*)这么慢,我该怎么办? 有时你会发现,随着系统 ...
- MySQL 笔记整理(20) --幻读是什么,幻读有什么问题?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 20) --幻读是什么,幻读有什么问题? 我们先来看看表结构和初始化数据 ...
- MySQL 笔记整理(19) --为什么我只查一行的语句,也执行这么慢?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 19) --为什么我只查一行的语句,也执行这么慢? 需要说明一下,如果M ...
- MySQL 笔记整理(11) --怎么给字符串字段加索引?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 11) --怎么给字符串字段加索引? 日常工作中的登录系统,你很可能会使 ...
随机推荐
- Sqoop葵花宝典
Sqoop葵花宝典 基于Sqoop1.x 场景 导入流程 graph LR A[RDBMS] -->|Sqoop| B(Hive) 导出流程 graph LR A[Hive] -->|Sq ...
- c++编译错误C2971:"std::array":array_size:包含非静态存储不能用作废类型参数;参见“std::array”的声明
在Qt5中这段代码编写有两种方式:一个编译成功,一个失败 成功版本: static constexpr size_t block_size = 0x2000;//8KB static constexp ...
- 实验5 Spark SQL编程初级实践
今天做实验[Spark SQL 编程初级实践],虽然网上有答案,但都是用scala语言写的,于是我用java语言重写实现一下. 1 .Spark SQL 基本操作将下列 JSON 格式数据复制到 Li ...
- go 语言的序列化与反序列化
与c 语言一样, 在网络编程中, go语言同样需要进行序列化与反序列化 在c语言中, 通常需要一块内存缓冲区用来收 发数据.缓冲区一般定义成char *buff类型. 当需要发送 数据时, 直接使用m ...
- 使用cAdvisor+Influxdb+Grafana监控系统
今天准备开始研究研究当前非常流行的Grafana+Influxdb监控系统,两者都是非常轻量级的应用但是功能却异常强大,可以说Grafana在作图显示方面真的毫不逊色与Cacti. 组件介绍 cA ...
- JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建
本篇博客就完整的来聊一下如何在Eclipse中创建的Maven Project.本篇博客是JavaEE开发的开篇,也是基础.本篇博客的内容干货还是比较多的,而且比较实用,并且都是采用目前最新版本的工具 ...
- 产品经理教你如何构建电商电销 CRM 系统
在电销或网销行业中老板们会经常问到,上个月渠道投放花了多少钱,来了多少量,转化率怎么样,获得了多少新线索,获客成本如何,销售额是多少? 劈天盖地的各种数据需求飞来,没有一个像样的系统该如何是好?这时候 ...
- [Swift]LeetCode55. 跳跃游戏 | Jump Game
Given an array of non-negative integers, you are initially positioned at the first index of the arra ...
- [Swift]LeetCode174. 地下城游戏 | Dungeon Game
The demons had captured the princess (P) and imprisoned her in the bottom-right corner of a dungeon. ...
- [Swift]LeetCode564. 寻找最近的回文数 | Find the Closest Palindrome
Given an integer n, find the closest integer (not including itself), which is a palindrome. The 'clo ...