select * from table where id=1 lock in share mode; select * from table where id=1 for update; update table set name = 'zhangsan' where id = 1; delete from table where id = 1;
「间隙锁:」
❝
即 Gap 锁,区间锁, 仅仅锁住一个索引区间(「开区间」)。
在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。
❞
对于非唯一索引的当前读,会加 Gap 锁,如下:
-- seq_id 是非唯一索引 select * from table where seq_id=3 lock in share mode; select * from table where seq_id=3 for update; update table set name = 'zhangsan' where seq_id = 3; delete from table where seq_id = 3;
「Next-Key锁:」
❝
next-key lock = record + gap lock,「左开右闭区间」,「InnoDB使用next-key lock来避免幻读问题」。
❞
举例来说:
假设 MySQL 表数据如下:
id
seq_id
4
1
5
3
6
5
7
7
8
9
当执行下面的语句时:
select * from table where seq_id=3 lock in share mode;
关键:对于insert操作来说,若发生唯一约束冲突,则需要对冲突的唯一索引加上 Share Record Lock + Gap Lock。(即使是RC事务隔离级别)
我们从时间线维度分析:
「事务T2」 insert into t7(id,a) values(26,10) 语句 insert 成功,持有a=10 的 X 行锁(X locks rec but not gap) ;
「事务T1」 insert into t7(id,a) values(30,10),因为T2 的第一条insert已经插入a=10的记录,事务T1的 insert a=10 则发生唯一约束冲突,需要申请对冲突的唯一索引 a=10加上Share Record Lock + Gap Lock (也即是 lock mode S waiting ) 这是一个间隙锁会申请锁住(4,10)之间的gap区域。从这里会发现,即使是RC事务隔离级别,也同样会存在Next-Key Lock锁,从而阻塞并发。所以,「此时事务 T1 持有(4,10)的 Gap Lock,并且等待 a=10上的 share lock。」
「事务T2」 insert into t7(id,a) values(40,9) 该语句插入的 a=9 ,需要先获取插入意向Gap锁(4,10),的值在 「事务T1申请的gap锁(4,10)之间」,故需事务T2的第二条insert语句要等待事务T1的Gaplock锁释放,在日志中显示 lock_mode X locks gap before rec insert intention waiting。所以,「此时事务 T2 持有a=10上的 X lock,并且等待(4,10)的插入意向Gap Lock。」