关于Mysql中的MVCC与幻读
2020-11-12 本文已影响0人
蓝调_4f2b
2020年秋招已经快结束了,在将近3个月各种大厂的轮番轰炸下,总算收获了东哥和雷总的offer并存活了下来。这篇简述我想着重记录一下秋招中遇到过很多次,每次都让我极为头痛的问题,Mysql InnoDB引擎下到底能不能有效防止幻读?
对于这个问题我最开始天真的相信了下面这张图片。
t014a29791d0aa151ad.jpg
然而每当我自信的说出可重复读不可防止幻读发生,面试官们总会留出小伙子可惜了的表情,伴随着质疑的那句你再想想?
啊。。。(我搜索尽我脑中关于Mysql的知识,说出了MVCC,但是MVCC也不能完全解决啊。。。)
。。。
就这样,我本着强烈的求知欲(求生欲)好好的研究了半天幻读的原理。
- 可重复读级别
主要通过MVCC方式避免脏读与不可重复读的现象出现,介于已提交读和可串行化之间的隔离级别,为InnoDB的默认隔离级别。
很多文章认为MySQL的锁为一种悲观锁机制,通过对于读取数据的行进行加锁,锁住数据本身而防止,从而防止其他事务修改正在读取的数据行,而于此同时如果有其他事务在数据行的间隙间插入数据的话,会造成幻读的情况。 - MVCC(多版本并发控制)
然而MySQL在漫长的发展中通过乐观锁的优化慢慢的解决了这个问题。MVCC的基本原理为通过保存数据在某时刻的快照实现。一个事务无论运行多长的时间,在同一个事务中均可看到数据一致的视图。MVCC会在每行数据后添加两个额外的隐藏值(数据创建时间,数据过期时间)。通过这两个时间号对事务进行期间查找的数据进行判断。这就将原先的悲观锁机制优化为了类似CAS的乐观锁机制,通过这种方式减少锁的使用,大大提升数据的操作效率。 - 快照读与当期读
通过上述锁优化机制,可重复读隔离级别下,MySQL可以通过版本号进行判断,并在有并发冲突时,将历史版本的数据呈现给用户,这种读取数据的方式在MySQL中被成为快照读。我们平时使用的select * from 一般为快照读,这种读数据的防守可以减少加锁带来的开销。而对于对数据修改的操作(insert, delete, update),我们采用当前读的方式进行。在当前读方式下,数据库会事先加入锁,锁住数据,保证数据绝对为最新。 - Next-Key锁
在上述事务操作过程中,数据库会为相应的数据行添加两种锁,行锁及间隙锁。通过间隙锁防止数据行间插入新的数据,实现防止幻读的效果。 - 不能防止幻读的情况
所以,当我们回归主题时,我们可不可以说可重复读级别可以防止幻读的发生?
答案是不完全能。。。当我们考虑正常情况时,可重复读确实可以通过MVCC及Next-key机制较好的完成防止幻读的任务,然而若出现如下的情况。
specialcase.png
(1)a事务先select,b事务insert确实会加一个gap锁,但是如果b事务commit,这个gap锁就会释放(释放后a事务可以随意操作),
(2)a事务再select出来的结果在MVCC下还和第一次select一样,
(3)接着a事务不加条件地update,这个update会作用在所有行上(包括b事务新加的),
(4)a事务再次select就会出现b事务中的新行,并且这个新行已经被update修改了.
参考文章:
事务隔离级别中的可重复读能防幻读吗?
文章出处:https://www.cnblogs.com/CoderAyu/p/11525408.html