跟面试官侃半小时MySQL事务隔离性,从基本概念深入到实现!

1.事务隔离性的基本概念

1.1 什么是ACID中的Isolation,隔离性

Isolation,隔离性,也有人称之为并发控制(concurrency control)。事务的隔离性要求每个事务读写的对象对其他事务都是相互隔离的,也就是这个事务提交前,这个事务的修改内容对其他事务都是不可见的。事务的隔离性,主要是解决不同事物之间的相互读写影响。

所谓的读写影响注意分为三种:

  • 脏读:读到了别的事务尚未提交(commit)的变更,别人没提交,我读到了。
  • 不可重复读:别的事务提交了变更,被当前事务读到了。然后导致本事务多次select的结果不一样,读到了别的事务提交的内容。
  • 幻读:也是读到了别的事务提交的内容,但是跟上面的不同之处在于,读到了原本不存在的记录。注意,不可重复读,主要是读到了别的事务update的内容。而幻读,是读到了别的事务insert的内容。

1.2 隔离性的隔离级别

为了解决事务隔离性的问题,数据库一般会有不同的隔离级别来解决相应的读写影响。

  • 读未提交:一个事务B还没提交,它的修改就被别的事务A读到了。
  • 读已提交:一个事务B提交后,它的修改被其他事务A看到了。
  • 可重复读:一个事物B提交前和提交后,事务A都无法读到事务B的变更。
  • 串行化:对同一行记录,当出现不同事物的读写冲突时,是通过串行化的方式解决的,后一个事务必须等前一个事务完成才能执行。

不同隔离级别能够解决不同的隔离性问题。
在这里插入图片描述

需要注意的是,这是标准事务隔离级别的定义。在MySQL的innodb引擎中,在可重复读级别下,通过mvcc解决了幻读的问题,具体实现我们后面再讲。

同时,需要注意的是,到目前为止,我们说的读,都是”快照读”,普通的select。后面我们还会提到“当前读”,是不一样的哦。

2.事务隔离性的实现

要实现事务的隔离性,需要了解两个方面的内容,一个是锁,一个是多版本并发控制(MVCC)。

2.1 事务的行锁

InnoDB中,实现了两种标准的 行级锁

  • 共享锁(S Lock),也叫读锁,允许事务读取一行数据。
  • 排它锁(X Lock),也叫写锁,允许事务删除或者更新一行数据(注意,这里没有提到插入哦,插入涉及到幻读,可以看文章最后的说明)

普通select语句不会有任何锁,那么如何获得共享锁和排它锁呢?

  • Select … lock in share mode语句能够获得共享锁
  • Select … for update(特殊的select,用mysql简单实现分布式锁经常用它)、Update、delete语句能够获得排它锁

当一个事务A已经获得了行r的共享锁,那么另一个事务B可以立刻获得行r的共享锁,因为不会改变r的数值,这种叫做锁兼容。

如果这时候有事务C希望获得行r的排它锁,那么就必须等待事务A和事务B释放行r的共享锁之后,才能获得排它锁,这种叫做锁不兼容。
在这里插入图片描述

普通的select不会对行上锁,而select…lock in share mode会上共享锁,select…for update会上排它锁。

  • 对于普通的select的读取方式,称为”快照读“,也叫”一致性非锁定读“。
  • 对于带锁的select读取,或者update tb set a = a+1(读取a的当前值),称为“当前读”,也叫“一致性锁定读”。

如果在update、insert的时候,不能进行select,那么服务的并发访问性能就太差了。因此,我们日常的查询,都是“快照读”,不会上锁,只有在update\insert\“当前读”的时候,才会上锁。而为了解决“快照读”的并发访问问题,就引入了MVCC。

2.2 多版本并发控制MVCC

如果说上面的行锁是一种悲观锁,那么MVCC就是一种乐观锁的实现方式,而且是一种很常用的乐观锁实现方式。

所谓多版本,就是一行记录在数据库中存储了多个版本,每个版本以事务ID作为版本号。InnoDB 里面每个事务有一个唯一的事务 ID,是在事务开始的时候向InnoDB的事务系统申请的,并且按照申请顺序严格递增的。假如一行记录被多个事务更新,那么,就会产生多个版本的记录。

以某一行数据作为例子:
在这里插入图片描述

经过两次事务的操作,value从22变成了19,同时,保留了三个事务id,15、25、30。

在每个记录多版本的基础上,需要利用“一致性视图”,来做版本的可见性判断。

这里,我们要区分MySQL里面的两个”视图”概念:

  • 一个是view,通过语法create view … 实现,主要创建一个虚拟表,用来执行查询语句。
  • 一个是InnoDB用来实现mvcc的一致性视图(consistent read view),纯逻辑概念,没有物理结构,定义了在事务期间,你能看到哪些版本的数据。

我们全文提到的“视图”都是第二种,主要是支持InnoDB在“读已提交”和“可重复读”级别的并发访问问题。

  • “读未提及”级别下,没有一致性视图
  • “读已提交”级别下,会在 每个SQL开始执行的时候 创建一致性视图
  • “可重复读”级别下,会在 每个事务开始的时候 创建一致性视图
  • “串行化”级别下,直接通过加锁避免并发问题

下面,我们简单介绍一下创建一致性视图的逻辑。

以“可重复读”级别为例。

  • 当一个事务开启的时候,会向系统申请一个新事务id
  • 此时,可能还有多个正在进行的其他事务没有提交,因此在瞬时时刻,是有多个活跃的未提交事务id
  • 将这些未提交的事务id组成一个数组,数组里面最小的事务id记录为低水位,当前系统创建过的事务id的最大值+1记录为高水位
  • 这个数组array 和 高水位,就组成了“一致性视图”。

有了一致性视图后,我们就可以判断一行数据的多版本可见性了,无论是“读已提交”还是“可重复读”级别,可见性判断规则是一样的,区别在于创建快照(一致性视图)的时间。

在当前事务中,读取其他某一行的记录,对其中的版本号的可见性判断有五种情况(建议自己跟着捋一捋,挺重要的):

  • 如果版本号小于“低水位”,说明事务已经提交,那肯定 可见;
  • 如果版本号大于“高水位”,说明这行数据的这个事务id版本是在快照后产生的,那肯定 不可见;
  • 如果版本号在事务数组array中,说明这个事务还没提交,所以 不可见;
  • 如果版本号不在事务数组array中,且低于高水位,说明这个事务已经提交,所以 可见;
  • 当然,无论什么时候,自己的事务id中的任何变化,都是可见的

可以看看下面这个例子,更容易理解。

系统创建过的事务id:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15

事务A启动,拍个快照

此时未提交的事务id有:7,8,9

一致性视图:数组array[7,8,9] + 高水位16(15+1)

对于任意一行数据的可见性判断如下。

  • 小于7的,可见
  • 大于16的,说明是快照后产生的,不可见
  • 10-15,不在数组array中,说明已经提交了,可见
  • 7,8,9在array中,说明未提交,不可见
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档

祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!

感谢大家的支持!!

image.png

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

[外链图片转存中…(img-2ziuOiW1-1712516147892)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值