mysql中的幻读
MySql可重复读(RR)隔离级别
当前读
通过间隙锁(next-key-lock)实现的
在执行select当前读语句的时候,会加上一个间隙锁,当有其他事务执行时,这个事务会被阻塞,无法执行,就保证了数据一致性问题;解决了幻读
1 | select ... lock in share mode |
快照读
通过MVCC实现的
多版本并发控制,多版本mysql维护行记录的多个版本,并发控制指不同的事务同一时间操作某一行记录时mysql控制返回多个版本行记录中的某个版本。
MVCC它有一个readview;在快照读的情况下,只会在事务开始时创建一个readview,后续的查询操作都是复用这个readview;当然这个readview它有自己的策略,它维护了几个值,活跃事务的id,最大事务的id,最小事务的id····;然后他就是用可见性算法拿当前事务的id和这些id进行一定规则的比较,来控制返回那个版本。
然后在RR级别隔离下,快照读也保证了数据一致性,很大程度上避免了幻读
1 | 普通select语句 |
是否解决了幻读?
我觉得主要看幻读的定义,在mysql45讲中,幻读是指“专门插入新的行”,那如果是这样认为的花,我觉得RR级别隔离下,无论是快照都和当前读都解决了幻读问题;
举个例子:事务A查询整个表的记录,这时候事务B执行插入操作,并且commit成功,这时候在事务A中再执行查询整个表的操作,那么它的查询结果和前面的是一样的;这个就是在快照读情况下保证了事务的一致性。
再举个当前读的例子,比如同样事务A执行查询,事务B执行插入操作,这个时候事务B就阻塞了,执行不成功,这也保证了数据的一致性;如果定义幻读为“所有写操作”;
那么又有一些特别的场景出现了幻读;比如事务A执行快照读查询操作,这时事务B执行插入操作,并且成功commit,接着事务A中在执行update操作,更新刚刚插入的那行数据,接着在进行查询操作,这个时候查询结果比先前多了一行,是事务B插入的,这个时候相当于了出现了幻读。
这是因为之前在事务A中的查询操作是快照读,这个时候它生成了一个readview,但是update是当前读,他会读取到最新的数据,后续在查询又会生成重新的readview,所以后面在查询就多了一条数据,出现了幻读。
