笔记记录自林晓斌(丁奇)老师的《MySQL实战45讲》

(本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除)

8.a) --事务到底是隔离还是不隔离的?

  本周工作较忙,加上懒惰,拖更了,抱歉。

  接上文,分析下事务A的返回结果,为什么k=1.这里我们做如下假设:

  1. 事务A开始前,系统里面只有一个活跃事务,其ID是99
  2. 事务A,B, C的版本号分别是100,101,102,且当前系统里面只有四个事务。
  3. 三个事务开始前,(1,1)这一行数据的row trx_id是90

  这样,事务A的视图数组就是[99,100],事务B的视图数组是[99,100,101],事务C的视图数组是[99,100,101,102]。为了简化分析,先把其他的干扰语句去掉,只画出事务A查询逻辑有关的操作。

  

    图4,事务A查询数据逻辑图

  从图中可以看到,第一个有效更新的是事务C,把数据从(1,1)更新成了(1,2)。这个时候,这个数据的最新版本的row trx_id是102,而90这个版本已经成为了历史版本。第二个有效更新的事务是B,把数据从(1,2)改成了(1,3)。这时候,这个数据的最新版本(即row trx_id)是101,而102又成为了历史版本。你可能已经注意到了,在事务A查询的时候,其实事务B还没有提及,但是它生成的(1,3)这个版本的记录已经成为了当前版本了。但这个版本对事务A必须是不可见的,否则就变成脏读了。

  现在事务A要来读数据了,它的视图数组是[99,100].当然,读数据都是从当前版本读起的。所以,事务A查询语句的数据流程是这样的:

  • 找到(1,3)的时候,判断出row trx_id = 101,比高水位大,在红色区域,不可见。
  • 找到上一个历史版本,row trx_id = 102,比高水位大,处于红色区域,不可见。
  • 接着查找上一个历史版本,row trx_id = 90,比低水位小,处于绿色区域,可见。

  这样,虽然期间这一行数据被修改过,事务A不论在什么时候查询,看到的这个行数据的结果都是一致的,即一致性读。以上判断流程是从代码逻辑转译过来的,如你所见,用于人肉分析很麻烦。另一种较好理解的说法是,对于一个数据版本,一个事务视图来说,除了自己的更新总是可见外,有三种情况。

  1. 版本未提交,不可见。
  2. 版本已提交,但是是在视图创建后提交的,不可见。
  3. 版本已提交,且是在视图创建前提交的,可见。

  现在我们再来看一下图4中的查询结果,事务A的查询语句的视图数组是在事务A启动的时候生成的,此时:

  • (1,3)未提交,不可见。
  • (1,2)已提交,但是是在视图数组创建之后提交的,不可见。
  • (1,1)是在视图创建前提交的,可见。

更新逻辑:

  你可能有个疑问,事务B的update语句,如果按照一致性读,结果不对呀?如图5所示,事务B的视图数组是先生成的,之后事务C才提交,不是应该看不见(1,2)吗,怎么能算出(1,3)来?

  

  图5,事务B的更新逻辑

  是的,如果事务B在更新操作之前先查询一下,返回的k的值确实是1.但是,当它要更新数据的时候,就不能再历史版本上更新了,否则事务C的更新就丢失了。因此,事务B此时的set k=k+1是在(1,2)的基础上进行的操作。这里用到了这样一条规则:更新数据都是先读后写,而这个读,只能读当前的值,称为“当前读”(current read). 因此,在更新的时候,当前读拿到的数据是(1,2),更新后生成了新版本的数据(1,3),这个心版本的row trx_id是101.所以,在执行事务B查询语句的时候,一看自己的版本号是101,最新数据的版本号也是101,是自己更新的,可以直接使用,所以查询得到的k的值是3。这里我们提到了一个概念,叫作当前读。其实,除了update语句外,select语句如果加锁,也是当前读. 所以,如果把事务A的查询语句select * from t where id = 1修改一下,加上lock in share model或for update,也都是可以读到版本号是101的数据,返回k的值是3.下面中两个select语句,就是分别加了读书(s锁,共享锁)和写锁(x锁,排它锁)。

mysql > select k from t where id = 1 lock in share mode;
mysql > select k from t where id = 1 for update;

  再往前一步,假设事务C不是马上提交的,而是变成了下面的事务C,会怎样呢?

  

    图6,事务A,B,C'的执行流程。

  事务C'不同的是,更新后没有马上进行提交,在它提交之前,事务B的更新语句先发起了。前面说过了,虽然事务C'还没提交,但是(1,2)这个版本也已经生成了,并且是当前的最新版本,那么事务B的更新语句会如何处理呢?这个时候就用到我们之前提过的“两阶段锁协议”了。事务C'没提交,也就是(1,2)这个版本上的写锁还没释放。而事务B是当前读,必须要读最新版本,而且必须加锁,因此就被锁住了,必须等到事务C'释放这个锁,才能继续它的当前读。

  

    图7,事务B更新逻辑图(配合事务C')

  至此,我们把一致性读,当前读和行锁就串起来了。现在我们回到开头的问题,事务的可重复读能力是怎么实现的呢?可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。而读提交的罗辑和可重复读的罗辑类似,它们最主要的区别在于:

  • 在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都公用这个一致性视图
  • 在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。

 

小结:

  InnoDB的行数据有多个版本,每个数据版本有自己的row trx_id,每个事务或者语句有自己的一致性视图,普通的查询语句是一致性读,一致性读会根据row trx_id和一致性视图确定数据版本的可见性。

  • 对于可重复读,查询只承认在事务启动前就已经提交完成的数据。
  • 对于读提交,查询只承认在语句启动前就已经提交完成的数据。

  而当前读,总是读取已经提交完成的最新版本。

  

上期问题:

  如果你要删除一个表里面的10000行数据,有以下三种方式:

  1. 直接执行 delete from T limit 10000;
  2. 在一个连接中循环执行20次delete from T limit 500;
  3. 在20个连接中同时执行delete from T limilt 500;

  你会选择哪种方式,为什么呢?

  第二种方式相对较好一些。第一种方式里面,单个语句占用时间长,锁的时间也长,而且大事务还会导致主从延迟。第三种方式会人为造成锁冲突。

问题:

  我用下面的表结构和初始化语句作为实验环境,事务隔离级别是可重复读。现在,我要把”所有字段c和id值相等”的行的c值清0,但是发现了一个“诡异”的,改不掉的情况。请你构造出这种情况,并说明原理。复现出来以后吗,请你再思考一下,在实际的业务开发中有没有可能碰到这个情况?你的应用代码是会不会掉进这个“坑”里,你又是如何解决的呢?

mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, c) values(1,1),(2,2),(3,3),(4,4);

  

MySQL 笔记整理(8.b) --事务到底是隔离还是不隔离的?的更多相关文章

  1. 最全mysql笔记整理

    mysql笔记整理 作者:python技术人 博客:https://www.cnblogs.com/lpdeboke Windows服务 -- 启动MySQL net start mysql -- 创 ...

  2. MySQL 笔记整理(8.a) --事务到底是隔离还是不隔离的?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 8.a) --事务到底是隔离还是不隔离的? 这部分内容不太容易理解,笔者也是进行了多次阅读.因此引用原文: 之前有提到过,如果是在可 ...

  3. MySQL 笔记整理(1) --基础架构,一条SQL查询语句如何执行

    最近在学习林晓斌(丁奇)老师的<MySQL实战45讲>,受益匪浅,做一些笔记整理一下,帮助学习.如果有小伙伴感兴趣的话推荐原版课程,很不错. 1) --基础架构,一条SQL查询语句如何执行 ...

  4. MySQL 笔记整理(3) --事务隔离,为什么你改了我还看不见?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> 3) --事务隔离,为什么你改了我还看不见? 简单来说,事务就是要保证一组数据操作,要么全部成功,要么全部失败.在MySQL中,事务 ...

  5. MySQL 笔记整理(16) --“order by”是怎么工作的?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 16) --“order by”是怎么工作的? 在林老师的课程中,第15 ...

  6. MySQL 笔记整理(14) --count(*)这么慢,我该怎么办?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 14) --count(*)这么慢,我该怎么办? 有时你会发现,随着系统 ...

  7. MySQL 笔记整理(20) --幻读是什么,幻读有什么问题?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 20) --幻读是什么,幻读有什么问题? 我们先来看看表结构和初始化数据 ...

  8. MySQL 笔记整理(19) --为什么我只查一行的语句,也执行这么慢?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 19) --为什么我只查一行的语句,也执行这么慢? 需要说明一下,如果M ...

  9. MySQL 笔记整理(11) --怎么给字符串字段加索引?

    笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 11) --怎么给字符串字段加索引? 日常工作中的登录系统,你很可能会使 ...

随机推荐

  1. 小程序wx:for循环列表数量的限制

    数据有100条,我们只要页面显示一部分,就要通过index来限制.index<3,就是显示序列0,1,2这三条数据.具体写法: <block wx:for='{{list}}' wx:ke ...

  2. 服务器Windows 登录 出现401 错误

     Method 1: Disable the loopback checkThe first method is to disable the loopback check by setting th ...

  3. Where is the Marble? (寻找大理石上的数字)

    (先上题目) (题目描述)Raju and Meena love to play with Marbles. They have got a lot of marbles with numbers w ...

  4. 【安富莱专题教程第6期】SEGGER的J-Scope波形上位机软件,RTT模式波形上传速度可狂飙到500KB/S左右

    说明:1.在实际项目中,很多时候,我们需要将传感器或者ADC的数值以波形的形式显示.通常的解决办法是用串口上位机,USB接口上位机或者MDK的逻辑分析仪功能,使用这三种方式都比较繁琐.本期专题为大家讲 ...

  5. win7系统下dos界面无法自由调整大小

    刚开始在win7系统,在dos界面下做MySQL的实验,很多数据不能显示界面上,只能显示固定的大小,以为这是系统的原因,后来在网上查找了一些资料.终于发现可以自由调节dos界面大小的方法.下面给出截图 ...

  6. 使用BurpSuite进行双文件上传拿Webshell

    首先进入网站后台:(后台界面应该是良精CMS) <ignore_js_op> 在 添加产品 这一栏有个上传文件: <ignore_js_op> 选择一个*.jpg格式的图片进行 ...

  7. [Swift]LeetCode1028. 从先序遍历还原二叉树 | Recover a Tree From Preorder Traversal

    We run a preorder depth first search on the root of a binary tree. At each node in this traversal, w ...

  8. 起底区块链人脸识别黑马,一个没有人像的人脸识别:iFace Chain(爱妃链)

    近几年来,人脸识别技术可谓在移动互联网中得到了空前广泛应用,从银行APP免密转账,人脸快捷支付到证券人脸开户,人脸识别技术已经应用到了移动互联的诸多应用场景.互联网无处不在的今天,便捷与安全貌似是一个 ...

  9. 理解JavaScript的临时包装对象

    Javascript语言中的对象和基础类型string.number.boolean都可以使用"."符号访问属性和方法,但是本质上只有对象才可以使用".",那么 ...

  10. oracle调整内存大小

    1.查看已分配内存,看到memory_max_target为20GSQL> show parameter sga NAME                     TYPE     VALUE- ...