MySQL事务隔离级别的实现原理(2)

乐观锁,如它的名字那样,总是认为别人不会去修改,只有在提交更新的时候去检查数据的状态。通常是给数据增加一个字段来标识数据的版本。

有这样三种锁我们需要了解

Record Locks(记录锁):在索引记录上加锁。

Gap Locks(间隙锁):在索引记录之间加锁,或者在第一个索引记录之前加锁,或者在最后一个索引记录之后加锁。

Next-Key Locks:在索引记录上加锁,并且在索引记录之前的间隙加锁。它相当于是Record Locks与Gap Locks的一个结合。

假设一个索引包含以下几个值:10,11,13,20。那么这个索引的next-key锁将会覆盖以下区间:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

了解了以上概念之后,接下来具体就简单分析下REPEATABLE READ隔离级别是如何实现的

理论分析

之所以说是理论分析,是因为要是实际操作证明的话我也不知道怎么去证明,毕竟作者水平实在有限。

但是,这并不意味着我在此胡说八道,有官方文档为证。

MySQL事务隔离级别的实现原理

这段话的大致意思是,在默认的隔离级别中,普通的SELECT用的是一致性读不加锁。而对于锁定读、UPDATE和DELETE,则需要加锁,至于加什么锁视情况而定。如果你对一个唯一索引使用了唯一的检索条件,那么只需锁定索引记录即可;如果你没有使用唯一索引作为检索条件,或者用到了索引范围扫描,那么将会使用间隙锁或者next-key锁以此来阻塞其它会话向这个范围内的间隙插入数据。

作者曾经有一个误区,认为按照前面说MVCC下的增删查改的行为就不会出现任何问题,也不会出现不可重复读和幻读。但其实是大错特错。

举个很简单的例子,假设事务A更新表中id=1的记录,而事务B也更新这条记录,并且B先提交,如果按照前面MVVC说的,事务A读取id=1的快照版本,那么它看不到B所提交的修改,此时如果直接更新的话就会覆盖B之前的修改,这就不对了,可能B和A修改的不是一个字段,但是这样一来,B的修改就丢失了,这是不允许的。

所以,在修改的时候一定不是快照读,而是当前读。

而且,前面也讲过只有普通的SELECT才是快照读,其它诸如UPDATE、删除都是当前读。修改的时候加锁这是必然的,同时为了防止幻读的出现还需要加间隙锁。

一致性读保证了可用重复读

间隙锁防止了幻读

回想一下

1、利用MVCC实现一致性非锁定读,这就有保证在同一个事务中多次读取相同的数据返回的结果是一样的,解决了不可重复读的问题

2、利用Gap Locks和Next-Key可以阻止其它事务在锁定区间内插入数据,因此解决了幻读问题

综上所述,默认隔离级别的实现依赖于MVCC和锁,再具体一点是一致性读和锁。

演示

MySQL事务隔离级别的实现原理

MySQL事务隔离级别的实现原理

MySQL事务隔离级别的实现原理

MySQL事务隔离级别的实现原理

MySQL事务隔离级别的实现原理

MySQL事务隔离级别的实现原理

上面四幅截图对比,可以看到由于id是主键,用id作为检索条件时只锁定那一个索引记录。接下来,看索引范围的例子

MySQL事务隔离级别的实现原理

MySQL事务隔离级别的实现原理

这两幅截图,可以看出,由于没有使用唯一索引作为检索条件,导致不光锁定了索引记录,还锁定了索引之间的间隙,应该是是使用了next-key锁。

参考 https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c629c8acd7ae7af609c6bfd7010d54c6.html