开启第二个事务,在code=10之前的间隙中插入一条数据,看下这条数据是否能够插入。
mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test values(2,2);插入的时候,执行被阻塞,查看引擎状态:
mysql> show engine innodb status\G; ... ---TRANSACTION 366864, ACTIVE 5 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 793, OS thread handle 123145434963968, query id 730065 localhost 127.0.0.1 root update insert into test values(2,2) ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 63 page no 4 n bits 72 index code of table `test`.`test` trx id 366864 lock_mode X locks gap before rec insert intention waiting Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 8; hex 800000000000000a; asc ;; 1: len 8; hex 000000000000000a; asc ;; ------------------ ...插入语句被阻塞了,lock_mode X locks gap before rec,由于第一个事务锁住了1到10之间的gap,需要等待获取锁之后才能插入。
如果再开启一个事务,插入(0,0)
mysql> start transaction; mysql> insert into test values(0,0); Query OK, 1 row affected (0.00 sec)可以看到:指定的非唯一建索引的gap锁的边界是当前索引到上一个索引之间的gap。
最后给出锁定区间的示例,首先插入一条记录(5,5)
mysql> insert into test values(5,5); Query OK, 1 row affected (0.00 sec)开启第一个事务:
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这个非唯一键的二级索引来举例说明。