栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

MySQL是怎样运行的——第二十一章

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MySQL是怎样运行的——第二十一章

这一章主要是MVCC。建议好好看。

21.1

这一节是给个例子表

CREATE TABLE hero(
	number INT,
	name VARCHAr(100),
	country VARCHAr(100),
	PRIMARY KEY (number)
) Engine = InnoDB CHARSET = utf8;

INSERT INTO hero VALUES(1, '刘备', '蜀');

注意这个插入,事务id是80。

21.2

这一节讲事务隔离级别。
为了正确地实现隔离性,我们当然希望并发的事务是按照某些顺序一个一个执行的,互不干扰。如果我们能让多个事务并行执行的结果与串行执行的结果完全相同,我们就把这些事务的执行方式称为可串行化。实现可串行化通常需要对事务进行排序。

但是实现可串行化会对性能造成很大的影响。所以人们考虑是否可以牺牲一定的隔离级别换取性能的提升。

一致性问题

因为我们希望牺牲隔离性换取性能,所以可能发生以下的问题:

脏写:事务一修改一个值,没提交,事务二可以把它修改成另一个值。这种情况绝对不允许。脏读:事务一修改一个值,事务二读取。然后事务一中止,事务二提交。事务二读到了不存在的值。不可重复读:事务一读一个值,事务二修改且提交,事务一再读,值变了。幻读:范围扫描,前后两次扫描出来的结果数不一样。

定义有很多,不同定义的划分方法不太一样。其实还有很多引发一致性问题的现象,不一一列举了。

在SQL标准中,定义了四个隔离级别,每个隔离级别解决不同的问题:

对Oracle来说,只支持READ COMMITTED 和 SERIALIZABLE。
对MySQL来说,四种都支持,并且在可重复读级别很大程度上解决了幻读的问题。

默认也是可重复读级别。

下面说一下设置,这部分没什么好整理的,直接把书上内容贴过来就好了。

21.3

这一节是最重要的,讲MVCC。

首先往demo表中插入一条记录。

在后面的图示中会隐去这条insert undo日志。

下面假设发生了更新

由于每进行一次更新操作就会诞生一条日志,所以形成的链条是这样的

其实这些深色的记录都不是完整的记录。他们只记载了相对于上一条记录更改过什么。不过由于记录了更改过什么,所以进行一些简单的加加减减是能够得到完整记录的。这里为了简便就直接画出完整记录了。

这种记录同一条记录的不同版本的机制叫MVCC。

ReadView

对于读未提交来说,由于允许读到别的线程没提交过的数据,所以直接读就可以了。
对于串行化来说,访问记录要加锁。

对于读已提交和可重复读来说,读记录时候要保证能读到已经提交的事务修改过的记录。换句话说就是计算版本链中哪个记录是可见的。

为此,InnoDB大叔提出了ReadView的概念。主要包含4个内容

m_ids:在生成ReadView时,当前系统中活跃的读写事务idmin_trx_ids:生成ReadView时,当前系统中活跃的读写事务的id的最小值max_trx_ids:系统要分给下一个事务的id(类似于一种全局的最大值的思想)creator_trx_id:生成ReadView的事务的id

有了ReadView以后,在访问某条记录的时候,只需要做如下判断。

如果自己是creator,永远可以读最新数据。如果记录被访问的版本的id小于min_trx_ids,则代表已经提交,可以访问。如果记录被访问的版本的id大于max_trx_ids,则代表事务在ReadView后面开启,不可访问。如果在最大最小值中间,则要看是否在m_ids里面,在的话说明创建ReadView的时候生成该版本的事务还是活跃的,不可访问。不在说明已经提交,可以访问。

如果一个版本的记录不可见,则顺着链条找下一个记录即可。

读已提交和可重复读的区别就是生成ReadView的时机不同。读已提交每次读都生成ReadView,读未提交只在第一次读时生成ReadVIew。如果开启事务时使用START TRANSACTION WITH ConSISTENT SNAPSHOT开启事务,会在执行语句后立即生成ReadView,而不是第一次读时生成。

二级索引

在二级索引上读的时候,没有trx_id和roll_pointer列,怎么确定记录的版本呢?
其实是每个记录所在页面里有一个PAGE_MAX_TRX_ID属性,如果当前事务id大于这个属性,就说明这个记录一定是已经提交的了,可以访问,否则需要回表。

结尾补充一下,前面说过delete操作分为两个阶段,这就是为了形成版本链。如果完全删除,则别的事务的读会受到影响。
此外,只有普通的读时,MVCC才生效。

21.4

这一节讲purge。
每个事务提交后,会把这个事务产生的一组update undo日志会插入到history链表里。这些记录也就没有释放。
同样的,一些被删除的记录也只是经历了第一个阶段,也没完全释放。
释放undo log空间、完全删除记录的操作就叫做purge。
那什么时候这些undo记录以及被删掉的记录可以完全释放呢?

其实就是根据ReadView来确定的,如果最早的ReadView生成时,事务已经提交,则这些记录就可以完全释放掉了。

为了确定事务提交顺序。设计者规定一个事务提交的时候要给它一个事务no。而ReadView里其实也保存了一个事务no,代表的也是系统下一个要使用的事务no值。

MySQL设计者把ReadView连成一个链表,在purge的时候,把最早的ReadView取出来(如果一个都没有,就现在生成一个),看看视图的事务no与一组undo 日志的事务no谁大。如果undo日志的事务no小于读视图的事务no,就说明这组undo日志没用了,可以释放undo日志的空间,并且可以完全删除记录。

所以如果有个很早就开启的事务一直不提交,undo log就会积累的越来越多。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/756291.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号