问题:
什么是幻读?
MySQL解决幻读了么?
幻读场景
场景一
现象:事务A明明查出来没有数据ID为3的数据,但是插入的时候却报了主键冲突的问题,出现幻读的场景
场景二
事务A在更新name为cc的数据之前明明查出来只有一条匹配的,但是更新结果却对两条数据产生了影响,出现了幻读的场景。
当前读和快照读
如果只看上面的两种场景的话其实我们可以下结论说MySQL在RR级别下没有解决幻读的问题,但是网上关于幻读的讨论一大堆,而且官方也没有给出一个明确的定性,到底是什么原因呢?这时候就要引出MySQL的当前读与快照读
快照读
在RR级别下,通过MVCC机制,可以让数据变得可重复读,而MVCC实现原理就是在开启事务后的第一次select操作的同时创建一份快照,接下来再执行select操作就是从快照中读取的。
注意:
第一次select的时候才会创建快照,你可以测试在开启事务A之后,先不执行select操作,这时候让事务B插入一条数据,接着事务A再执行select的话会查出来事务B插入的数据。
快照读针对的是select操作,从上面可以看到我们在事务A的最后一步select的话读取到的是快照中的数据,但是update,delete,insert读取到的却不是
快照读虽然解决了可重复读,但是他可能让读出来的数据不是最新的数据,而是历史数据
当前读
MySQL对于update、insert、delete使用的都是当前读的模式,再执行这几个操作的时候读取到的都是当前最新的数据。别的事务提交的数据也可以查到,甚至别的数据未提交的数据也可以查到,你可以测试下在事务A插入一个id为10的数据但是不提交,这时候如果事务B同样插入一个数据为10的数据的话就会阻塞,你可以认为加了一个排他锁,你也可以站在另外一个角度认为他查询到了其他的事务操作的数据。
MySQL三种行锁
Record Lock:对单个行记录进行加锁
Gap Lock:间隙锁,对一个区间范围加锁,但不包含数据本身,所以叫做间隙,而GAP锁提出的原因就是为了解决幻读问题
Next-Key Lock:他是第一种和第二种的合体,这样就可以在锁住范围的同时锁住本身,对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
结论
再了解了上面的问题以后我们再来看看为什么有的人认为MySQL在RR级别下解决了幻读,有的认为没有解决。
我认为这是对幻读的定位、认知不同。
在认为解决的人看来。幻读不能把快照读和当前读混起来操作,这是两种使用方式。幻读应该是在快照读或当前读本身的场景下,两次读取结果一致,而MySQL在快照读的情况下通过MVCC已经解决了,在当前读的情况MySQL通过next-key来解决了。
在认为没有解决的人看来。幻读应该不关心快照读还是当前读,只要第二次读(这个读比较广泛)读出了第一次读没有的数据就叫幻读,在这种定义下,我们可以从上面的两种场景看出,MySQL没有解决。
参考