众所周知,MySQL在InnoDB下有四种隔离级别:
读未提交
读已提交
可重复读
串行化
其中可重复读可以避免脏读和不可重复读。但是对于幻读(a事务在b事务insert提交前后,两次分别读到的数据不一致),却存在争议。
实验分析:
准备数据表
create table tb(
id int not null primary key auto_increment,
num int not null
);
insert into tb(id, num) values(1, 11), (2, 22), (3, 33);
select * from tb;
执行结果:
id num
1 11
2 22
3 33
查看隔离级别,将隔离级别调到RR。
set global transaction isolation level repeatable read;
select @@global.tx_isolation;
select @@tx_isolation; -- 当前会话的隔离级别必须为RR
打开两个会话(隔离级别都要为RR)。按表格顺序执行。
1、
对于事务a读取结果:(在事务a内读到了新插入的行记录–phantom)
注:MySQL5.7和8.0都是这种执行结果。
begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。第一种启动方式,一致性视图是在执行第一个快照读语句时创建的;第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。
注:RR隔离级别下,事务启动的时候创建一个视图。只要本事务不对某一数据更新,那么每一次读到的数据是一样的。
2、还是基础数据表
实验结果:a事务的两次select查询到的结果相同,在后一次查询中没有返回新插入id=4的那条记录。
据此,很多人判断说RR隔离级别下“不存在”幻读。
但果真如此吗?
出现上面的试验结果,是因为在RR隔离级别事务下,Mysql会对前一次select的结果快照。所以第二次select其实是快照读(这也正是RR隔离级别下能够避免不可重复读的策略)。
3、基础数据表
实验结果:在a事务的第二次select中出现了b事务新插入的id=4的记录。
Ps:假如给第二次的select查询上锁(无论是共享锁还是排它锁),也会得到同样的结果,都会令快照失效。
参考:
https://www.jianshu.com/p/70477870d8f5
https://www.jianshu.com/p/4c02a3a2e9d2
极客时间45讲