http://blog.sina.com.cn/s/blog_502c8cc40100y0wj.html
MVCC由于其实现原理,只支持read committed和repeatable read隔离等级,下面分别举例详细说明:
每次开始之前,都先执行如下的语句:
create database if not exists mydb;
use mydb;
drop table if exists emp;
create table `emp` ( `empno` int(11) not null auto_increment, `ename` varchar(20) default null, Primary key (empno)) engine=innodb default charset=gbk;
insert into emp values(100, "yuxiangang") ;
insert into emp values(200,"2zhaoyinggang");
insert into emp values(300,"3yihongbin");
3.1 read committed隔离等级
说明:session 1和session 2表示访问同一个数据库的两个不同的会话.行号用来代表不同的语句执行的时间点.
行号 |
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,两次获取的数据内容不一样,称之为不可重复读. |
3.2 repeatable read隔离等级
注意:先执行开头的所有sql语句.
行号 |
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 | +-------+-------+ |
说明: 当所有事务都提交后,他们看到的结果都是一样的 |