事务隔离级别与MVCC

Posted 宇宙警备队

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事务隔离级别与MVCC相关的知识,希望对你有一定的参考价值。

mysql常见的几种存储引擎及其特点:


MyISAM

独立于操作系统的,这说明可以轻松地将其从Windows服务器移植到Linux服务器;

无法处理事务

MyISAM存储引擎特别适合在以下几种情况下使用:

1.选择密集型的表。MyISAM存储引擎在筛选大量数据时非常迅速,这是它最突出的优点。

2.插入密集型的表。MyISAM的并发插入特性允许同时选择和插入数据。例如:MyISAM存储引擎很适合管理邮件或Web服务器日志数据


InnoDB

健壮的事务型存储引擎

在以下场合下,使用InnoDB是最理想的选择:

1.更新密集的表。InnoDB存储引擎特别适合处理多重并发的更新请求。

2.事务。InnoDB存储引擎是支持事务的标准MySQL存储引擎。

3.自动灾难恢复。与其它存储引擎不同,InnoDB表能够自动从灾难中恢复。

4.外键约束。MySQL支持外键的存储引擎只有InnoDB。

5.支持自动增加列AUTOINCREMENT属性。

一般来说,如果需要事务支持,并且有较高的并发读取频率,InnoDB是不错的选择。


MEMORY 

存储介质是系统内存

一般在以下几种情况下使用Memory存储引擎:

1.目标数据较小,而且被非常频繁地访问。在内存中存放数据,所以会造成内存的使用,可以通过参数maxheaptablesize控制Memory表的大小,设置此参数,就可以限制Memory表的最大大小。

2.如果数据是临时的,而且要求必须立即可用,那么就可以存放在内存表中。

3.存储在Memory表中的数据如果突然丢失,不会对应用服务产生实质的负面影响。


ARCHIVE

Archive是归档的意思,在归档之后很多的高级功能就不再支持了,仅仅支持最基本的插入和查询两种功能。

Archive拥有很好的压缩机制,它使用zlib压缩库,在记录被请求时会实时压缩,所以它经常被用来当做仓库使用。


一、 事务隔离级别

事务

一系列对数据访问与更新的操作 所组成的程序执行单元;



事务特性:ACID

原子性(atomicity  [ˌætəˈmɪsɪti]

一个事务必须被视为一个不可分割的的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚;

一致性(consistency  [kənˈsɪstənsi]

数据库总是从一个一致性的状态转换成另外一个一致性状态;

不一致状态:比如 数据库系统在运行中发生故障,有些事务尚未完成就被迫中断,对数据库的修改只有一部分写入物理数据库,这时数据库就是处于一种不一致的状态;

所以说,事务是一致性的单位,如果事务中某个动作失败了,系统可以自动撤销事务,返回初始化的状态。


隔离性(isolation  [ˌaɪsəˈleʃən]        

一个事务所做的修改在最终提交前,对其它事务是不可见的;

持久性(durability  [ˌdjʊrəˈbɪlətɪ]

事务一旦提交,其所做的修改就会永久保存到数据库中。



隔离级别

标准SQL规范中定义了4种隔离级别,不同的隔离级别对事务的处理不同;

READ UNCOMMITED(未提交读)

事务中的修改,即使没有提交,对其它事务也都是可见的。

事务可以读取未提交的数据,也被称为脏读。

从性能上讲,不会比其它级别好太多,却缺乏其它级别的好处,实际应用中很少使用,如Oracle就不支持该级别。

READ COMMITED(提交读)

大多数的数据库系统默认级别都是该级别(MySql是REPEATABLE READ);

只允许获取已经提交的数据。

REPEATABLE READ(可重复读)

保证在事务处理过程中,多次读取同一个数据时,其值都和事务开始时刻是一致的。

可能出现幻读:如  系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

SERIALIZABLE(串行化)

事务只能一个接一个进行处理,不能并发执行;

最高的隔离级别;

隔离级别

脏读

不可重复读

幻读

数据库默认隔离级别

READ UNCOMMITED(未提交读)


READ COMMITED(提交读)

Oracle和SQL Server

REPEATABLE READ(可重复读)

MySQL

SERIALIZABLE(串行化)


事务隔离级别相关的命令

查询隔离级别

-- select @@txisolation;


设置隔离级别

-- set global transaction isolation level 级别字符串;  



二、MVCC


引入了并发事务之后,如果不对事务的执行进行控制就会出现各种各样的问题,常见的并发控制机制主要有三种:

观并发控制、观并发控制、多版本并发控制;

悲观并发控制(Pessimistic Concurrency Control

悲观并发控制其实是最常见的并发控制机制,也就是锁;(后续“锁”专题分享,此处不赘述)



乐观并发控制(Optimistic Concurrency Control


乐观并发控制也叫乐观锁,但是它并不是真正的锁,基于时间戳的协议能够保证所有冲突的读写操作都能按照时间戳的大小串行执行,在执行对应的操作时不需要关注其他的事务只需要关心数据项对应时间戳的值就可以了;

(后续“锁”专题分享,此处不赘述)



多版本并发控制(MultiVersion Concurrency Control, MVCC


大多数事务型存储引擎实现的都不是简单的行级锁,一般都同时实现了MVCC;

可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低; 

虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只需要锁定必要的行,下面主要介绍InnoDB的实现。


基本原理:

MVCC 的实现,是通过保存数据在某个时间点的快照来实现的

对普通的SELECT不加锁 即在某个时刻对事系统打快照记下所有活跃读写事务ID,之后读操作根据事务ID与快照中的事务ID进行比较,判断可见性。


Innodb中的隐藏列

InnoDB的内部实现中为每一行数据增加了几个隐藏列用于实现MVCC。

列名

作用

DBTRXID

系统版本号,标记了最新更新这条行记录的transaction id,每处理一个事务,其值自动+1

DBROLLPTR

指向当前记录项的rollback segment的undo log记录,找之前版本的数据就是通过这个指针

DBROWID

隐含ID,当由innodb自动产生聚集索引时,聚集索引包括这个DBROWID的值,否则聚集索引中不包括这个值.这个用于索引当中;

如果表没有定义主键那么DATAROWID作为主键列,否则行结构中没有DATAROWID列。

DELETE BIT

标识该记录是否被删除



名词延伸:


名词

解释

undo log

用于存放数据修改被修改前的值 ,可以用来在事务失败时进行rollback;

redo log

记录某数据块被修改后的值,可以用来恢复未写入data file的已成功事务更新的数据

rollback segment

在Innodb中,undo log被划分为多个段,具体某行的undo log就保存在某个段中,称为回滚段

data file

数据库文件

db buffer

数据库缓存

log buffer

日志缓存

log file

磁盘日志文件


在不考虑redo log 的情况下利用undo log工作的简化过程为:


序号

动作

1

开始事务

2

记录数据行数据快照到undo log

3

更新数据

4

将undo log写到磁盘

5

将数据写到磁盘

6

提交事务

1)为了保证数据的持久性,数据要在事务提交之前持久化

2)undo log的持久化必须在在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来回滚事务

下面分别以select、delete、 insert、 update语句来说明

SELECT

Innodb检查每行数据,确保他们符合两个标准:

1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行

2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除

符合了以上两点则返回查询结果。

INSERT

InnoDB为每个新增行 记录当前系统版本号作为行版本号。

DELETE

InnoDB为每个删除行 记录当前系统版本号作为行的删除标识

UPDATE

InnoDB插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

下面演示下事务对某行记录的更新过程:

1. 初始数据行

F1~F6是某行列的名字,1~6是其对应的数据。后面三个隐含字段分别对应该行的事务号和回滚指针,假如这条数据是刚INSERT的,可以认为ID为1,其他两个字段为空。

2.事务1更改该行的各字段的值

当事务1更改该行的值时,会进行如下操作:

用排他锁锁定该行

记录redo log

把该行修改前的值Copy到undo log,即上图中下面的行

修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行

3.事务2修改该行的值

与事务1相同,此时undo log,中有有两行记录,并且通过回滚指针连在一起。

因此,如果undo log一直不删除,则会通过当前记录的回滚指针回溯到该行创建时的初始内容,所幸的时在Innodb中存在purge线程,它会查询那些比现在最老的活动事务还早的undo log,并删除它们,从而保证undo log文件不至于无限增长。


事务提交


当事务正常提交时Innodb只需要更改事务状态为COMMIT即可,不需做其他额外的工作;

而Rollback则稍微复杂点,需要根据当前回滚指针从undo log中找出事务修改前的版本,并恢复。如果事务影响的行非常多,回滚则可能会变的效率不高,根据经验值:事务行数在1000~10000之间,Innodb效率还是非常高的。


适用范围


MVCC只在REPEATABLE READ(可重复读)和READ COMMITED(提交读)两个隔离级别下工作。

因为:

READ UNCOMMITED总是读取最新的数据行,而不是符合当前事务版本的数据行;

SERIALIZABLE则会对所有读取的行都加锁;


MVCC优缺点

MVCC在大多数情况下代替了行锁,实现了对读的非阻塞,读不加锁,读写不冲突。

缺点是每行记录都需要额外的存储空间,需要做更多的行维护和检查工作。


思考:

Innodb的实现方式是:

事务以排他锁的形式修改原始数据

把修改前的数据存放于undo log,通过回滚指针与主数据关联

修改成功(commit)啥都不做,失败则恢复undo log中的数据(rollback)


当修改数据时是否要排他锁定,如果锁定了还算不算是MVCC? 


Innodb的实现真算不上MVCC,因为并没有实现核心的多版本共存,undo log中的内容只是串行化的结果,记录了多个事务的过程,不属于多版本共存。但理想的MVCC是难以实现的,当事务仅修改一行记录使用理想的MVCC模式是没有问题的,可以通过比较版本号进行回滚;但当事务影响到多行数据时,理想的MVCC据无能为力了。

比如,如果Transaciton1执行理想的MVCC,修改Row1成功,而修改Row2失败,此时需要回滚Row1,但因为Row1没有被锁定,其数据可能又被Transaction2所修改,如果此时回滚Row1的内容,则会破坏Transaction2的修改结果,导致Transaction2违反ACID。




参考:

《高性能MySql》


https://draveness.me/database-concurrency-control

http://www.cnblogs.com/chenpingzhao/p/5065316.html

https://www.jianshu.com/p/a3d49f7507ff

https://blog.csdn.net/chen77716/article/details/6742128

http://www.360doc.com/content/14/0821/09/12904276403505950.shtml



以上是关于事务隔离级别与MVCC的主要内容,如果未能解决你的问题,请参考以下文章

事务隔离级别与MVCC

MYSQL的事务隔离级别,MVCC,readView和版本链小结

Mysql原理篇之事务隔离级别和MVCC--13

分布式事务MVCC事务隔离级别

性能优化|MVCC通俗理解与事务隔离级别实战操作

MySQL里的undo log, MVCC以及事务隔离级别