
首先需要明确的就是“幻读”概念: 隔离级别是可重复读,在一个事务中前后两次查询,查到了其他事务insert进来的数据。
下面来论证一下可重复读下幻读的解决方案
先明确一下,for update语法就是当前读,也就是查询当前已经提交的数据,并且是带悲观锁的。没有for update就是快照读,也就是根据readView读取的undolog中的数据。
如果按照以上猜想,那么整个执行结果就违背了 可重复读 的隔离级别了。
那么我们再假设select * from TABLE where d = 5 for update这条语句锁定的是所有被扫描到的数据。
这是因为T2阶段的update会被阻塞住,毕竟所有被扫描到的记录都被锁定了。
按照上述推理过程,很显然,即使锁定所有扫描到的数据行,也依然存在幻读的情况。违背了 可重复读 的隔离级别。
针对这个情况,我们要解决幻读的问题,那么就要求针对所有被扫描的记录行以及还不存在的d=5的记录行都给锁住。
至此,当前查询结果完全满足 可重复读 的隔离级别。
通过以上推论,我们可以总结一下,在可重复读的隔离级别下,解决幻读除了需要锁定所有扫描到的记录行外,还需要锁定行之间的间隙,也就是通过间隙锁来解决幻读的问题。
幻读是指:在一个事务中,读取到了其他已经提交的事务插入的数据行。MySQL在解决脏读、不可重复的读时候,使用了MVCC一致性视图,同时配合行锁来解决。
至于幻读的解决方式,MySQL引入了临键锁,通过间隙锁可以避免在两个行之间插入数据,从而避免了一个事务在读取的过程中,读取到其他事务插入的数据行。
接上篇 事务隔离级别和幻读 ,留了个坑,没想到竟然过了10天,时间不注意真的过的好快。顺便提下,图片链接是属于网站的,开发自己的图床迫在眉睫,万一哪天迁移就要做很多额外工作,一些概念或者思路用图片表达更直观清楚。
回到正题,之前提到一般情况下MySQL的InnoDB引擎在可重复读的情况下是没法保证不出现幻读的,但实际情况是MySQL可以通过加锁来防止幻读的出现,这种锁定通过Next-key机制来实现,是属于记录锁和间隙锁(Gap锁)的结合。
引申,行级别锁的三种算法:
举个存在唯一索引和辅助索引的例子做说明:
执行 select * from test where b = 3 for update
存在两个索引,分别加锁,唯一主键列a加record lock , 辅助索引列b加next-key lock (1,3) 以及给下一个值的区间(3,6)加gap锁;
因此在另一个事务里执行以下语句都会阻塞,具体分析:
第一个阻塞因为加了唯一索引的record lock a = 5;
第二个主键插入4,符合条件,但是根据辅助索引b 的范围, b = 2 在(1,3)中,同样阻塞;
第三个a =6 不在主键a锁定范围,b = 5 也不在辅助索引b 的范围(1,3)中,但在另一个gap锁范围(3,6)中,因此也阻塞;
这种锁定情形下,可以执行的包括类似语句:
insert的特殊情况
对于insert 会检查下一条记录是否被锁定,如上述例子有 select * from test where b = 3 for update 插入 insert into test select 2,2 会检测到b = 3 已经被锁定,而 insert into test select 2,0 可以执行;
[1]:《MySQL技术内幕:InnoDB存储引擎》-第六章:锁
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)