一致性事务--关于MVCC机制的理解

Posted 一只蓝色猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一致性事务--关于MVCC机制的理解相关的知识,希望对你有一定的参考价值。

文章目录

一、MVCC简介

    1.1 什么是MVCC

    1.2 MVCC解决什么问题?

    1.3 MVCC实现

二、MVCC实现原理

    2.1简单的小例子

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

三、对于MVCC的总结



一、MVCC简介



引入:几种锁

读锁:也叫共享锁、S锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S 锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

写锁:又称排他锁、X锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。

表锁:操作对象是数据表。mysql大多数锁策略都支持(常见mysql innodb),是系统开销最低但并发性最低的一个锁策略。事务t对整个表加读锁,则其他事务可读不可写,若加写锁,则其他事务增删改都不行。

行级锁:操作对象是数据表中的一行。是MVCC技术用的比较多的,但在MYISAM用不了,行级锁用mysql的储存引擎实现而不是mysql服务器。但行级锁对系统开销较大,处理高并发较好。


1.1 什么是MVCC


MVCC (Multiversion Concurrency Control),是一种多版本并发控制机制。


1.2 MVCC解决什么问题?


  • 大部分支持行锁的事务引擎,如 InnoDB,Falcon以及PBXT不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来来一起使用.

  • 大家都应该知道,锁机制可以控制并发操作,但是其系统开销较大,而MVCC可以在大多数情况下代替行级锁,使用MVCC,能降低其系统开销,从而大大提高数据库系统的并发性能.


1.3 MVCC实现


MVCC是通过保存数据在某个时间点的快照来实现的,不同存储引擎的MVCC实现是不同的,典型的有乐观并发控制和悲观并发控制.



二、MVCC实现原理



下面,我们通过InnoDB的MVCC实现来分析MVCC是怎样进行并发控制的, 

innodb MVCC主要是为Repeatable-Read事务隔离级别做的。

InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的ID),没开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID.下面看一下在Repeatable-Read隔离级别下,MVCC具体是如何操作的.


2.1简单的小例子


create table demo( 
id int primary key auto_increment,
name varchar(20));


假设系统的版本号从1开始.


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



INSERT

InnoDB为新插入的每一行保存当前系统版本号作为版本号. 

第一个事务ID为1;

start transaction;
insert into demo values(NULL,'yang') ;
insert into demo values(NULL,'long');
insert into demo values(NULL,'fei');
commit;


对应在数据库中的表如下(后面两列是隐藏列,我们通过查询语句并看不到)

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 undefined
2 long 1 undefined
3 fei 1 undefined


SELECT

InnoDB会根据以下两个条件检查每行记录:


a、InnoDB只会查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的. 

b、行的删除版本要么未定义,要么大于当前事务版本号,这可以确保事务读取到的行,在事务开始之前未被删除. 

只有a,b同时满足的记录,才能返回作为查询结果.


DELETE

InnoDB会为删除的每一行保存当前系统的版本号(事务的ID)作为删除标识. 


看下面的具体例子分析: 

第二个事务ID为2;

start transaction;
select * from demo;  //(1)
select * from demo;  //(2)
commit;


假设1

假设在执行这个事务ID为2的过程中,刚执行到(1),这时,有另一个事务ID为3往这个表里插入了一条数据; 

第三个事务ID为3;

start transaction;
insert into demo values(NULL,'tian');
commit;


这时表中的数据如下:

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 undefined
2 long 1 undefined
3 fei 1 undefined
4 tian 3 undefined


然后接着执行事务2中的(2),由于id=4的数据的创建时间(事务ID为3),执行当前事务的ID为2,而InnoDB只会查找事务ID小于等于当前事务ID的数据行,所以id=4的数据行并不会在执行事务2中的(2)被检索出来,在事务2中的两条select 语句检索出来的数据都只会下表:

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 undefined
2 long 1 undefined
3 fei 1 undefined


假设2

假设在执行这个事务ID为2的过程中,刚执行到(1),假设事务执行完事务3后,接着又执行了事务4; 

第四个事务ID为4:

start   transaction;  
delete from demo where id=1;
commit;


此时数据库中的表如下:

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 4
2 long 1 undefined
3 fei 1 undefined
4 tian 3 undefined


接着执行事务ID为2的事务(2),根据SELECT 检索条件可以知道,它会检索创建时间(创建事务的ID)小于当前事务ID的行和删除时间(删除事务的ID)大于当前事务的行,而id=4的行上面已经说过,而id=1的行由于删除时间(删除事务的ID)大于当前事务的ID,所以事务2的(2)select * from yang也会把id=1的数据检索出来.所以,事务2中的两条select 语句检索出来的数据都如下:

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 4
2 long 1 undefined
3 fei 1 undefined


UPDATE

InnoDB执行UPDATE,实际上是新插入了一行记录,并保存其创建时间为当前事务的ID,同时保存当前事务ID到要UPDATE的行的删除时间.


假设3

假设在执行完事务2的(1)后又执行,其它用户执行了事务3,4,这时,又有一个用户对这张表执行了UPDATE操作: 

第五个事务ID为5:

start  transaction;
update demo set name='Long' where id=2;
commit;


根据update的更新原则:会生成新的一行,并在原来要修改的列的删除时间列上添加本事务ID,得到表如下:

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 4
2 long 1 5
3 fei 1 undefined
4 tian 3 undefined
2 Long 5 undefined


继续执行事务2的(2),根据select 语句的检索条件,得到下表:

id name 创建时间(事务ID) 删除时间(事务ID)
1 yang 1 4
2 long 1 5
3 fei 1 undefined


还是和事务2中(1)select 得到相同的结果.



三、对于MVCC的总结



一般我们认为MVCC有下面几个特点:

  • 每行数据都存在一个版本,每次数据更新时都更新该版本

  • 修改时Copy出当前版本随意修改,各个事务之间无干扰

  • 保存时比较版本号,如果成功(commit),则覆盖原记录;失败则放弃copy(rollback)


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

 

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

 

理想MVCC难以实现的根本原因在于企图通过乐观锁代替二段提交。修改两行数据,但为了保证其一致性,与修改两个分布式系统中的数据并无区别,而二提交是目前这种场景保证一致性的唯一手段。二段提交的本质是锁定,乐观锁的本质是消除锁定,二者矛盾,故理想的MVCC难以真正在实际中被应用,Innodb只是借了MVCC这个名字,提供了读的非阻塞而已。


参考文章:

https://blog.csdn.net/whoamiyang/article/details/51901888

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



当前浏览器不支持播放音乐或语音,请在微信或其他浏览器中播放

以上是关于一致性事务--关于MVCC机制的理解的主要内容,如果未能解决你的问题,请参考以下文章

关于postgresql中事务隔离的总结

深入理解MVCC与BufferPool缓存机制

深入理解MVCC与BufferPool缓存机制

INNODB MVCC

MySQL----事务transaction

MySQL----事务transaction