MVCC是一种多版本并发控制(Multi-Version Concurrency Control)机制。大多数的MYSQL事务型存储引擎如:InnoDB,Falcon以及PBXT都不使用一种简单的行锁机制。事实上,他们都和MVCC多版本并发控制来一起使用。大家都应该知道,锁机制可以控制并发操作,但是其系统开销较大,而MVCC可以在大多数情况下代替行级锁,使用MVCC,能降低其系统开销。
MVCC的具体实现:
Innodb的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了这个行的创建时间,另一个保存的是行的删除时间。这里存储的并不是实际的时间值,而是系统版本号,每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID。下面看一下在(可重复读)隔离级别下,MVCC具体是如何操作的。
1、创建user表;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
2、开启事务,在user表中插入3条数据
start transaction;
insert into user (name) values('zhangsan');
insert into user (name) values('lisi');
insert into user (name) values('wangwu');
commit;
数据对应如下,后面两列用select语句是看不到的
3、select语句
InnoDB会根据以下两个条件检查每行记录,查询结果必须同时满足这两个条件。
a) 只会查询 创建时间(事务ID)小于或等于当前事务ID的行,这样可以确保当前事务读取的行,要么是在事务开始前已经存在,要么是事务自身插入或修改过。
b) 删除事务ID为undefined或着删除ID大于当前事务ID的行,这可以确保当前事务读取到的行,在当前事务开始之前未被删除。
4、delete语句
InnoDB会为删除的每一行保存当前系统的版本号(事务的ID)作为删除标识。
举例,开启第二个事务ID=2,查询user全部数据:
start transaction;
select * from user; //(v1)
select * from user; //(v2)
commit;
(添加举例)当事务ID=2的事务运行到v1的语句时,执行了一条插入语句,插入语句的事务递增ID=3
start transaction;
insert into user (name) values('zhaoliu');
commit;
此时数据库结果为:
根据InnoDB查询语句的限制条件,事务2的查询只会查询创建事务ID小于或者等于2的行以及删除事务ID为undifined或者大于2的行,id=4的数据在执行事务2中的( v2)时不会被检索出来。所以,事务2的v1和v2查询结果都为:
(删除举例)如果,在事务2执行到v1语句是,事务3插入了一条数据,马上开启事务4删除id=1的数据:
start transaction;
delete from user where id=1;
commit;
此时,数据库数据如下,id=1的数据删除事务ID=4,表示实在事务4中删除的数据:
接着执行事务ID为2的事务(v2),根据select检索条件知道,它只会检索创建事务ID小于当前事务ID和删除事务ID大于当前事务的行。检索出数据如下:
5、update语句
InnoDB执行UPDATE,实际上就是新插入了一行记录,并保存期创建时间(事务ID)为当前事务的ID,同事将旧的行中删除时间(事务ID)保存为当前事务ID;即添加一行新数据,将旧的行数据标记为删除。
(更新举例)举例,接着上面的例子,假设在事务2执行到v1是,分别执行了事务3插入和事务4删除,然后接着执行事务5更新操作:
start transaction;
update user set name='tianqi' where id=2;
commit;
此时数据库数据如下,插入一条新数据,创建时间为5,将之前id=2的旧数据的删除时间修改为当前的事务id为5:
接着执行事务ID为2的事务(v2),根据select检索条件知道,它只会检索创建事务ID小于当前事务ID和删除事务ID大于当前事务的行。检索出数据如下:
MVCC的机制保证了可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。