InnoDB的锁机制深入理解(4)

对于code,可能的next-key锁的范围是:

(-∞,1] (1,5] (5,10] (10,+∞)

开启第一个事务,在code=5的索引上请求更新:

mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where code=5 for update; +----+------+ | id | code | +----+------+ | 5 | 5 | +----+------+ 1 row in set (8.81 sec)

之前在gap锁的章节中介绍了,code=5 for update会在code=5的索引上加一个record锁,还会在1<gap<5的间隙上加gap锁。现在不再验证,直接插入一条(8,8):

mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test values(8);

insert处于等待执行的状态,这就是next-key锁生效而导致的结果。第一个事务,锁定了区间(1,5],由于RR的隔离级别下next-key锁处于开启生效状态,又锁定了(5,10]区间。所以插入SQL语句的执行被阻塞。

解释:在这种情况下,被锁定的区域是code=5前一个索引到它的间隙,以及next-key的区域。code=5 for update对索引的锁定用区间表示,gap锁锁定了(1,5),record锁锁定了{5}索引记录,next-key锁锁住了(5,10],也就是说整个(1,10]的区间被锁定了。由于是for update,所以这里的锁都是X锁,因此阻止了其他事务中带有冲突锁定的操作执行。

如果我们在第一个事务中,执行了code>8 for update,在扫描过程中,找到了code=10,此时就会锁住10之前的间隙(5到10之间的gap),10本身(record),和10之后的间隙(next-key)。此时另一个事务插入(6,6),(9,9)和(11,11)都是不被允许的,只有在前一个索引5及5之前的索引和间隙才能执行插入(更新和删除也会被阻塞)。

3.5 插入意向锁 Insert Intention Locks

插入意向锁在行插入之前由INSERT设置一种间隙锁,是意向排它锁的一种。
在多事务同时写入不同数据至同一索引间隙的时,不会发生锁等待,事务之间互相不影响其他事务的完成,这和间隙锁的定义是一致的。

假设一个记录索引包含4和7,其他不同的事务分别插入5和6,此时只要行不冲突,插入意向锁不会互相等待,可以直接获取。参照锁兼容/冲突矩阵。
插入意向锁的例子不再列举,可以查看gap锁的第一个例子。

3.6 自增锁

自增锁(AUTO-INC Locks)是事务插入时自增列上特殊的表级别的锁。最简单的一种情况:如果一个事务正在向表中插入值,则任何其他事务必须等待,以便第一个事务插入的行接收连续的主键值。

我们一般把主键设置为AUTO_INCREMENT的列,默认情况下这个字段的值为0,InnoDB会在AUTO_INCREMENT修饰下的数据列所关联的索引末尾设置独占锁。在访问自增计数器时,InnoDB使用自增锁,但是锁定仅仅持续到当前SQL语句的末尾,而不是整个事务的结束,毕竟自增锁是表级别的锁,如果长期锁定会大大降低数据库的性能。由于是表锁,在使用期间,其他会话无法插入表中。

4 幻读

这一章节,我们通过幻读,逐步展开对InnoDB锁的探究。

4.1 幻读概念

解释了不同概念的锁的作用域,我们来看一下幻读到底是什么。幻读在RR条件下是不会出现的。因为RR是Repeatable Read,它是一种事务的隔离级别,直译过来也就是“在同一个事务中,同样的查询语句的读取是可重复”,也就是说他不会读到”幻影行”(其他事务已经提交的变更),它读到的只能是重复的(无论在第一次查询之后其他事务做了什么操作,第二次查询结果与第一次相同)。

上面的例子都是使用for update,这种读取操作叫做当前读,对于普通的select语句均为快照读。

当前读,又叫加锁读,或者 阻塞读。这种读取操作不再是读取快照,而是读取最新版本并且加锁。
快照读不会添加任何锁。

官方文档对于幻读的定义是这样的:

原文:The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
手动无脑翻译:所谓的幻影行问题是指,在同一个事务中,同样的查询语句执行多次,得到了不同的结果,这就是幻读。例如,如果同一个SELECT语句执行了两次,第二次执行的时候比第一次执行时多出一行,则该行就是所谓的幻影行。

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

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