多版本并发控制技术已经被广泛运用于各大数据库系统中,如Oracle,MS SQL Server 2005+, Postgresql, Firebird, Maria等等,开源数据库MYSQL中流行的INNODB引擎也采用了类似的并发控制技术.本文就将结合实例来解析不同事务隔离等级下INNODB的MVCC实现原理.1 MVCC概述
行号
|
session 1
|
session 2
|
|
1
|
set transaction isolation level read committed;
|
|
|
2
|
start transaction;
|
|
|
3
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1yuxiangang |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
+-------+-------+
|
|
|
4
|
|
set transaction isolation level read committed;
|
|
5
|
|
start transaction;
|
|
6
|
|
update emp set ename=1 where empno=100;
delete from emp where empno=200;
|
|
说明: 修改一行,然后删除一行,但是事务不提交.
|
|||
7
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1yuxiangang |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
+-------+-------+
|
|
|
说明:会话2的事务没有提交,所以会话1看不到会话2的事务对数据库数据的修改.但是实际上修改已经发生,会话1获取的被修改或者删除的数据,都来自于回滚段.这是通过MVCC来实现的.
|
|||
8
|
|
commit;
|
|
说明: 会话2提交
|
|||
9
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 300 | 3yihongbin |
+-------+-------+
|
|
|
说明:当事务2提交以后,由于会话1采用的是read committed隔离等级,所以会话2的提交马上会被会话1的事务看见.对于会话1来说,第一次执行select * from emp where empno>=100;与第二次执行该语句,两次看到的结果不一样,第一次读看到了3行,第二次只看到了2行,就像发生了幻觉,称之为幻读;第一次看到100对应的ename为1yuxiangang,第二次看到的100对应的是1,两次获取的数据内容不一样,称之为不可重复读.
|
行号
|
session 1
|
session 2
|
session 3
|
session 4
|
||
1
|
set transaction isolation level repeatable read;
|
|
|
|
||
2
|
start transaction;
|
|
|
|
||
3
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1yuxiangang |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
+-------+-------+
|
|
|
|
||
4
|
|
|
|
set @@session.autocommit=1;
|
||
说明: 这里让会话4可以自动提交,便于观察它对前面3个会话的影响
|
||||||
|
|
|
|
update emp set ename=1 where empno=100;
insert into emp values(400,"4chj");
|
||
说明: 会话4先更新一行数据,然后插入一行数据,并自动提交
|
||||||
5
|
select * from emp where empno>=100;
查询的结果为: +-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1yuxiangang |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
+-------+-------+
|
|
|
|
||
说明: 会话1执行查询,两次查询得到的结果一样.它看不到会话4对数据库的修改,虽然会话4的事务已经提交.这是因为会话4的事务是在会话1的事务之后才开始.从这里也可以看出,repeatable read实现了可重复读
|
||||||
6
|
|
set transaction isolation level repeatable read;start transaction;
|
|
|
||
7
|
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
| 400 | 4chj |
+-------+-------+
|
|
|
||
说明: 会话2是在会话4之后开始的,所以它看到了会话4对数据库的修改.同时可以看到,相同的查询语句,不同的事务来执行的时候,得到的结果不一样.会话2与会话3执行相同的查询就得到不一样的结果.
|
||||||
8
|
|
|
|
update emp set ename=2 where empno=200;
|
||
9
|
|
|
set transaction isolation level repeatable read;start transaction;
|
|
||
10
|
|
|
select * from emp where empno>=100;查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4chj |
+-------+-------+
|
|
||
说明: 同样,这个会话查询到的结果与会话1和会话2的结果也不一样.而且会话3看到了会话4对数据库的修改.
|
||||||
11
|
|
|
|
update emp set ename=4 where empno=400;
|
||
12
|
|
|
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4 |
+-------+-------+
|
||
说明: 事务总是可以看到自身对数据库数据的修改,尽管别的事务可能看不到这种修改
|
||||||
13
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1yuxiangang |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
+-------+-------+
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2zhaoyinggang |
| 300 | 3yihongbin |
| 400 | 4chj |
+-------+-------+
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4chj |
+-------+-------+
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4 |
+-------+-------+
|
||
说明: 从上面的结果可以很清晰的看到:会话1,2,3,4执行相同的语句,即使是在同一时刻,他们看到的数据都可能不一样:对于empno为100的行,有 100 1yuxiangang 和 100 1两个版本;对于empno为200的行,有 200 2zhaoyinggang 和200 2两个版本…,而每一行数据都可能存在多个版本,那么这些行组合起来得到的结果集的版本就更是不计其数,这就是数据库多版本的由来.MVCC就是通过事务发生的不同的时间点,与数据行的版本来进行对比,从而取回与事务开始的时间点相一致的数据,来实现非阻塞的一致读.
|
||||||
14
|
commit;
|
commit;
|
commit;
|
commit;
|
||
15
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4 |
+-------+-------+
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4 |
+-------+-------+
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4 |
+-------+-------+
|
select * from emp where empno>=100;
查询的结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 100 | 1 |
| 200 | 2 |
| 300 | 3yihongbin |
| 400 | 4 |
+-------+-------+
|
||
说明: 当所有事务都提交后,他们看到的结果都是一样的
|