开启第一个事务:
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test where code between 1 and 10 for update; +----+------+ | id | code | +----+------+ | 1 | 1 | | 5 | 5 | | 10 | 10 | +----+------+ 3 rows in set (0.00 sec)第二个事务,试图去更新code=5的行:
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> update test set code=4 where code=5;执行到这里,如果第一个事务不提交或者回滚的话,第二个事务一直等待直至mysql中设定的超时时间。
3.4 Next-key Locks Next-key锁Next-key锁实际上是Record锁和gap锁的组合。Next-key锁是在下一个索引记录本身和索引之前的gap加上S锁或是X锁(如果是读就加上S锁,如果是写就加X锁)。
默认情况下,InnoDB的事务隔离级别为RR,系统参数innodb_locks_unsafe_for_binlog的值为false。InnoDB使用next-key锁对索引进行扫描和搜索,这样就读取不到幻象行,避免了幻读的发生。
幻读是指在同一事务下,连续执行两次同样的SQL语句,第二次的SQL语句可能会返回之前不存在的行。
当查询的索引是唯一索引时,Next-key lock会进行优化,降级为Record Lock,此时Next-key lock仅仅作用在索引本身,而不会作用于gap和下一个索引上。
查看Next-key锁 Next-key锁的作用范围如上述例子,数据表test初始化了row(1,1),row(10,10),然后插入了row(5,5)。数据表如下:
mysql> select * from test; +----+------+ | id | code | +----+------+ | 1 | 1 | | 5 | 5 | | 10 | 10 | +----+------+ 3 rows in set (0.00 sec)由于id是主键、唯一索引,mysql会做优化,因此使用code这个非唯一键的二级索引来举例说明。
对于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锁的第一个例子。