我之前一直质疑网传mysql innodb的RR隔离级别下,next-key lock解决了幻读这种说法的准确性。

这次理顺了。

例子准备:

mysql RENAME COLUMN TO报错_共享锁

mysql RENAME COLUMN TO报错_mysql_02

场景一、select * from user 

order

sessionA

sessionB

结果

解释

T1

start transaction;

 

 

 

T2

 

start transaction;

 

 

T3

 

select * from user;

成功

有三条

快照读

T4

insert into user (mobile_phone,age) values ('1234',121);

 

成功

 

T5

 

select * from user;

成功

只有三条

因为是快照读,且事务A还没提交。

此处是MVCC避免了幻读

T6

 

select * from user where id = 1;

成功

 

T7

 

select * from user where id = 3;

成功

 

T8

 

select * from user lock in share mode;

等待

当前读,加共享锁,而事务A锁定了刚insert的id4

 

commit;

 

 

此时事务B的等待结束,T8的结果是有四条,看到了事务A的insert的内容

出现幻读

 

 

select * from user;

成功

只有三条

因为是快照读。

此处是MVCC避免了幻读

 

 

select * from user lock in share mode;

成功

有四条,看到了事务A的insert的内容

出现幻读

看一下next-key lock的含义:当sql语句按照范围查询非唯一索引列,并且有数据命中的时候会给索引加上临键锁,锁住命中索引项的前一个索引到命中索引项的后一个索引之间的一个左开右闭区间

 该场景结论:

select *  from user 是快照读,是MVCC避免了幻读

select * from user lock in share mode,当前读,触发了幻读。

善意的理解,这种查询SQL并没有走非唯一索引,也不是范围查询,所以next-key lock并没有生效。

换句话说:准确的说,mysql innodb 的 RR隔离下,一样会出现幻读,next-key lock和MVCC只解决了部分幻读的场景。

场景二、select * from user where age >60 and age < 100

order

sessionA

sessionB

结果

解释

T1

start transaction;

 

 

 

T2

 

start transaction;

 

 

T3

 

select * from user where age >60 and age < 100;

成功

有2条

快照读

T4

insert into user (mobile_phone,age) values ('1234',121);

insert into user (mobile_phone,age) values ('1234',50);

insert into user (mobile_phone,age) values ('1234',101);

insert into user (mobile_phone,age) values ('1234',61);

insert into user (mobile_phone,age) values ('1234',91);

 

 

 

T5

 

select * from user where age >60 and age < 100

成功

有2条

快照读

T6

 

select * from user where id = 1;

 

 

T7

 

select * from user where id = 3;

 

 

T8

 

select * from user where age >60 and age < 100 lock in share mode;

等待

当前读,加共享锁,而事务A锁定了刚insert的id45678

 

commit;

 

 

此时事务B的等待结束,T8的结果是有四条,看到了事务A的insert的内容

出现幻读

 

 

select * from user where age >60 and age < 100;

成功

只有三条

因为是快照读。

此处是MVCC避免了幻读

 

 

select * from user where age >60 and age < 100 lock in share mode;

成功

有四条,看到了事务A的insert的内容

出现幻读

该场景结论同场景一一样

场景三、 select * from user where age >60 and age < 100 lock in share mode;

order

sessionA

sessionB

结果

解释

T1

start transaction;

 

 

 

T2

 

start transaction;

 

 

T3

 

select * from user where age >60 and age < 100 lock in share mode;

成功

有2条

此时加上了next-key lock age=0 到 aget=120

T4

insert into user (mobile_phone,age) values ('1234',121);

 

成功

 

 

 

 

select * from user where age >60 and age < 100 lock in share mode;

 

有2条

该区间内本来也只有2条

 

 

select * from user where age >60 and age < 100;

 

有2条

该区间内本来也只有2条

 

insert into user (mobile_phone,age) values ('1234',0);

 

等待

因为T3加了next-key lock

 

UPDATE user  set mobile_phone = '11111' WHERE age = 120;

 

等待

因为T3加了next-key lock

T5

insert into user (mobile_phone,age) values ('1234',50);

 

等待

因为T3加了next-key lock

T7

insert into user (mobile_phone,age) values ('1234',61);

 

 

等待

因为T3加了next-key lock

T8

insert into user (mobile_phone,age) values ('1234',91);

 

等待

因为T3加了next-key lock

 

 

commit;

 

 

 

 

 

 

事务A等待结束,insert成功

 

 

 

 

 

该场景结论:

用当前读,非唯一索引范围查询,会加next-key lock,另外一个事务在该区间不能再insert值需等next-key lock释放

该场景下的确是next-key lock避免了幻读

总结

 MySQL Innodb RR隔离级别下能够解决幻读的场景及原因:

1.快照读,由MVCC解决幻读

2.当前读,只有在利用非唯一索引进行范围查询的时候,才会使用next-key lock,避免该范围内的幻读