mysql> show engine innodb status;
------------
TRANSACTIONS
------------
Trx id counter 14992
Purge done for trx's n:o < 14987 undo n:o < 0 state: running but idle
History list length 914
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started
MySQL thread id 50, OS thread handle 0x7f5d57e0a700, query id 1495 localhost root init
show engine innodb status
---TRANSACTION 14991, ACTIVE 17 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1
MySQL thread id 49, OS thread handle 0x7f5d57d88700, query id 1491 localhost root update
insert into t values(6)
------- TRX HAS BEEN WAITING 17 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 187 page no 4 n bits 80 index `idx_t` of table `test`.`t` trx id 14991 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000007; asc ;;
1: len 6; hex 00000000060c; asc ;;
------------------
---TRANSACTION 14989, ACTIVE 32 sec
2 lock struct(s), heap size 360, 5 row lock(s)
MySQL thread id 43, OS thread handle 0x7f5d57f0e700, query id 1489 localhost root
其中"locks gap"就表示阻塞insert语句的锁是gap锁,即范围锁。锁定的范围包括(-∞,4],(4,7](锁到操作行的下一个key,此处插入id=6,由于存在id=7的key,所以锁到7为止,这就是next-key的意思)。当测试插入或修改-1,0,5,6等小于7的值都会被阻塞,而插入或修改大于7的值就不会被阻塞。
如何判断锁定的范围大小?可以通过下面的查询语句:
mysql> select * from information_schema.INNODB_LOCKS\G
*************************** 1. row ***************************
lock_id: 2856:109:4:6
lock_trx_id: 2856
lock_mode: X,GAP
lock_type: RECORD
lock_table: `test`.`t`
lock_index: idx_t
lock_space: 109
lock_page: 4
lock_rec: 6
lock_data: 7
*************************** 2. row ***************************
lock_id: 421383739058184:109:4:6
lock_trx_id: 421383739058184
lock_mode: S
lock_type: RECORD
lock_table: `test`.`t`
lock_index: idx_t
lock_space: 109
lock_page: 4
lock_rec: 6
lock_data: 7
2 rows in set (0.000 sec)
lock_mode为"X+GAP",表示next-key lock算法。其中lock_data值为7,表示锁定了值为7的记录,这是最大锁定范围边界。lock_rec的值为6,表示锁定了6行记录,其中1,2,3,4,7共5行记录是通过gap锁锁定的范围,加上待插入的id=6(该行为key锁锁定),共锁定6行记录。
而如果使用的是大于号,由于操作任何一条记录,它的下一个key都会被锁定,这等价于锁定了整个无穷区间,即实现了表锁的功能。如下:
在会话1上执行:
# 首先回滚
rollback;
begin;
select * from t where id>10 lock in share mode;
在会话2执行:
insert into t values(0);
insert into t values(5);
insert into t values(100);
会发现任何插入都是阻塞的。即锁定的范围为(-∞,+∞),等价于锁定了整张表。
但是如果使用的等于号,那么在查找索引的时候发现只需锁定一条记录和下一条记录中间的范围即可。
在会话1执行:
# 首先回滚
rollback;
begin;
select * from t where id=5 lock in share mode;
在会话2执行:
insert into t values(0);
insert into t values(10);
会发现上述插入都是允许的。
但如果插入id=6的记录,则阻塞,因为锁定的范围为[5,7]区间。
也就是说,在有索引的情况下,如果是非具体的行锁,那么就会将能扫描到的索引键值内的所有范围加锁。
下面测试没有索引的情况。
2.无索引的情况
首先创建没有索引的表,然后插入一些分隔的记录。
create table ttt(id int);
insert into ttt values(1),(2),(3),(4),(7),(8),(12),(15);
在会话1上执行:
begin;
select * from ttt where id=4 lock in share mode;
在会话2上执行:
insert into ttt values(5);
insert into ttt values(100);
insert into ttt values(0);
会发现不管是插入哪些记录,都会被阻塞。因为没有索引键值的时候,自动隐式创建索引会锁定整个区间。查看下innodb的事务状态。