大家好,我是来自JDL京东物流技术发展部邢焕杰, 今天来分享一个京东面试真题,这也是我前阵子听我工位旁边高T(高,实在是高)面试候选人的时候问的一个问题,他问,你能说说 MySQL的事务吗?MVCC有了解吗?话不多说,本文就深度解析一下MySQL事务及MVCC的实现原理。
我们提供的服务有:成都网站制作、网站建设、外贸网站建设、微信公众号开发、网站优化、网站认证、方正ssl等。为1000多家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的方正网站制作公司
事务是什么?
就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。
事务的四大特性(简称ACID):
脏读(dirty read):就是一个A事务即便没有提交,它对数据的修改也可以被其他事务B事务看到,B事务读到了A事务还未提交的数据,这个数据有可能是错的,有可能A不想提交这个数据,这只是A事务修改数据过程中的一个中间数据,但是被B事务读到了,这种行为被称作脏读,这个数据被称为脏数据
不可重复读(non-repeatable read):在A事务内,多次读取同一个数据,但是读取的过程中,B事务对这个数据进行了修改,导致此数据变化了,那么A事务再次读取的时候,数据就和第一次读取的时候不一样了,这就叫做不可重复读
幻读(phantom read):A事务多次查询数据库,结果发现查询的数据条数不一样,A事务多次查询的间隔中,B事务又写入了一些符合查询条件的多条数据(这里的写入可以是update,insert,delete),A事务再查的话,就像发生了幻觉一样,怎么突然改变了这么多,这种现象这就叫做幻读
多个事务互相影响,并没有隔离好,就是我们刚才提到的事务的四大特性中的 隔离性(Isolation) 出现了问题 事务的隔离级别并没有设置好,下面我们来看下事务究竟有哪几种隔离级别
我们来看个例子,更加直观的了解这四种隔离级别和上述问题脏读,不可重复读,幻读的关系
下面我们讨论下当事务处于不同隔离级别情况时,V1,V2,V3分别是什么不同的值吧
读未提交 (RU): A事务可以读取到B事务修改的值,即便B事务没有提交。所以V1就是200
读提交(RC): 当B事务没有提交的时候,A事务不可以看到B事务修改的值,只有提交以后才可以看到
可重复读(RR): A事务多次读取数据,数据总和第一次读取的一样,
串行化(S): 事务A在执行的时候,事务B会被锁住,等事务A执行结束后,事务B才可以继续执行
MVCC(Multi-Version Concurrency Control)多版本并发控制,是数据库控制并发访问的一种手段。
- 特别要注意 MVCC只在 读已提交(RC) 和 可重复度(RR) 这两种事务隔离级别下才有效
- 是 数据库引擎(InnoDB) 层面实现的,用来处理读写冲突的手段(不用加锁),提高访问性能
MVCC是怎么实现的呢?它靠的就是版本链和一致性视图
那么这个版本链又是如何形成的呢,每条数据又是靠什么链接起来的呢?
其实是这样的,对于InnoDB存储引擎的表来说,它的聚簇索引记录包含两个隐藏字段
假如说当前数据库有一条这样的数据,假设是事务ID为100的事务插入的这条数据,那么此条数据的结构如下:
后来,事务200,事务300,分别来修改此数据:
所以此时的版本链如下:
我们每更改一次数据,就会插入一条undo日志,并且记录的roll_pointer指针会指向上一条记录,如图所示:
所以串成的链表就是 C -> B -> A -> 小杰 (从最新的数据到最老的数据)
需要判断版本链中的哪个版本是是当前事务可见的,因此有了一致性视图的概念。其中有四个属性比较重要
版本链中的当前版本是否可以被当前事务可见的要根据这四个属性按照以下几种情况来判断
如果某个版本数据对当前事务不可见,那么则要顺着版本链继续向前寻找下个版本,继续这样判断,以此类推。
注:RR和RC生成一致性视图的时机不一样 (这也是两种隔离级别实现的主要区别)
- 读提交(read committed RC) 是在 每一次select的时候生成ReadView的
- 可重复读(repeatable read RR)是在 第一次select的时候生成ReadView的
下面咱们一起来举个例子实战一下。
假如说,我们有多个事务如下执行,我们通过这个例子来分析当数据库隔离级别为RC和RR的情况下,当时读数据的一致性视图和版本链,也就是MVCC,分别是怎么样的。
- 假设数据库中有一条初始数据 姓名是java小杰要加油,id是1 (id,姓名,trx_id,roll_point),插入此数据的事务id是1
- 尤其要指出的是,只有这个事务操作了某些表的数据后当更改操作发生的时候(update,delete,insert),才会分配唯一的事务id,并且此事务id是递增的,单纯开启事务是没有事务id的,默认为0,creator_trx_id是0。
- 以下例子中的A,B,C的意思是将姓名更改为A,B,C 读也是读取当前时刻的姓名,默认全都开启事务,并且此事务都经历过某些操作产生了事务id
一个事务提交之后,它做的变更才会被其他事务看到
每次读的时候,ReadView(一致性视图)都会重新生成
此时这条数据的版本链如下:
同颜色代表是同一事务内的操作
来我们静下心来好好分析一下此时T4时刻事务300要读了,
究竟会读到什么数据?
当前最近的一条数据是,C,事务200修改的,还记得我们前文说的一致性视图的几个属性吗,和按照什么规则判断这个数据能不能被当前事务读。我们就分析这个例子。 此时 (
生成一致性视图ReadView)
min_trx_id 是 100: m_ids的最小值
max_trx_id 是 201: m_ids的最大值+1
当前数据的trx_id(事务id)是 200,符合min_trx_id<=trx_id 分析完第一个读,我们继续向下分析
此时这条数据的版本链如下:
此时 (重新生成一致性视图ReadView)
当前数据事务id是300,数据为D,符合min_trx_id<=trx_id
分析完第二个读,我们继续向下分析
此时这条数据的版本链如下:
此时 (重新生成一致性视图ReadView)
当前事务id是200,200
当隔离级别是读已提交RC的情况下,每次读都会重新生成 一致性视图(ReadView)
- T4时刻 事务300读取到的数据是 小杰
- T7时刻 事务400读取到的数据是 B
- T10时刻 事务300读取到的数据是 E
一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的
所以对于事务300来讲,它分别在T4和T10的时候,读取数据,但是它的一致性视图,
用的永远都是第一次读取时的视图,就是T3时刻产生的一致性视图
RR和RC的版本链是一样的,但是判断当前数据可见与否用到的一致性视图不一样
在此可重复读RR隔离级别下,
此时 (用的是第一次读时生成的一致性视图ReadView)
此时的版本链是
当前数据的事务id是200,数据是E,在当前事务活跃列表里面,所以数据不可见,根据回滚指针找到上个版本,发现事务id是300,当前事务也是300,可见,所以读取的数据是D
我们可以自己思考下,要是没有事务300这条更改的这条记录,又该怎么继续向下分析呢?
当隔离级别是可重复读RR的情况下,每次读都会用第一次读取数据时生成的一致性视图(ReadView)
- T4时刻 事务300读取到的数据是 小杰
- T7时刻 事务400读取到的数据是 B
- T10时刻 事务300读取到的数据是 D
新闻名称:我身边的高T,问了Java面试者这样的问题......
网站路径:http://www.csdahua.cn/qtweb/news6/124106.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网