MYSQL Innodb隔离级别:可重复读 为什么并未解决幻读

一.四个隔离级别                               二.可重复读导致的幻读(亲测)                  三.MVCC(Mutil-Version Concurrency Control)


在可重复读的隔离级别下,select是不能够查出其他事务已经提交的新增记录事务,那难道可重复读解决了幻读的问题吗?当然没有,隔离级别中,只有串行化隔离级别才解决了幻读。

一.四个隔离级别

先重温一下事务的隔离级别吧

Read Uncommitted(读取未提交内容)

在此隔离级别,所有事务都可以看到其他未提交事务的执行结果。

例如:程序员发现自己工资卡入账8000¥,他很开心,这个月咋这么多。可是下一秒,老板发现工资发错了,等他算算具体多少¥再说,然后反手就把这笔8000流水回滚了。当程序员再去看余额想让自己开心一下的时候,发现这个时候卡里只剩下了个寂寞,晚上,伤心的猿站在天台,点了支烟,自己抽一半,风也抽一半。

Read Committed(读取提交内容)

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

例如:程序员又在看自己的余额,发现两次查看的余额不一样,余额的数字咋还变多了。原来老板突然发现忘了给他发去年年终,给他结工资的事务提交了以后,在程序员第二次看余额之前,又提交了一个补发去年年终奖的事务更新了他的余额。

Repeatable Read(可重读)

这是MySQL InnoDB引擎隔离级别默认为REPEATABLE-READ,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

例如:有一天程序员的老婆大人在例行查程序员的银行卡消费流水,发现程序员正在疯狂消费!记录多了好多条新的,顷刻,怒发冲冠!晚上,他老婆准备好了家里最凹凸不平的键盘等程序员下班回家,执行家法...(后来发现原来是程序员偷偷买化妆品给自己的老婆,看在直男这么难得想浪漫一回的份上,逃过一劫!)

Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

二.可重复读导致的幻读(亲测)

先来看一个小例子,这个表一开始是空的

序号 事务A 事务B
1 begin; begin;
2   select * from student;
3 insert into student (Sname,Ssex,Sage,Ssum) VALUES ('试两下','男','86',55);  
4 commit;  
5   select * from student;
6   commit;

运行的结果是:事务B的 2和5,查询结果是一样的,似乎事务B没有查出来事务A已经提交的插入。貌似可重复读防止了幻读的出现????

其实并不是的,请继续看接下来的例子;

序号 事务A 事务B
1 begin; begin;
2 insert into student (Sname,Ssex,Sage,Ssum) VALUES ('试三下','女','23',23);  
3 commit;  
4   update student set Snum = 999 where Sage = 23;
5   select * from student;
6   commit;

运行的结果是:事务B的序号 4 更新成功 事务A序号 2 插入的数据行。

所以,可重复读并没有防止幻读的出现。可是为什么第一个例子中两次select的结果却“貌似”防止了幻读呢?

这就涉及到MVCC的一致性视图

三.MVCC(Mutil-Version Concurrency Control)

MVCC 多版本并发控制

每一次select,Innodb就会生成一致性视图(read view),一致性视图的内容如下

[未提交事务ID] [已经创建的最大事务ID]

这个一致性视图是怎么生成的呢,是根据版本链。版本链机制是快照读现象的实现。

那可能看到这里,可能有人会有一个问题,那为什么delete/update可以更新成功呢?因为可重复读本来不能防止幻读啊哈哈哈,不要忘了问题开始的地方。因为版本链机制,才导致了select不会查出其他事务已经提交的事务的结果。

所以,隔离级别:可重复读 不能防止幻读的出现,只是该隔离级别下的版本链机制,使得select操作达到了“防止幻读”的效果。

 

你可能感兴趣的:(数据库,mysql)