加锁分析
1.首先,执行事务1执行: begin;
insert into song_rank(songId,weight) values(15,100) on duplicate key update weight=weight+1;
会获得 gap锁(10,20),insert intention lock(插入意向锁)
2.接着,事务2执行: begin;
insert into song_rank(songId,weight) values(16,100) on duplicate key update weight=weight+1;
会获得 gap锁(10,20),同时等待事务1的insert intention lock(插入意向锁)。
3.再然后,事务3执行: begin;
insert into song_rank(songId,weight) values(18,100) on duplicate key update weight=weight+1;
会获得 gap锁(10,20),同时等待事务1的insert intention lock(插入意向锁)。
4.最后,事务1回滚(rollback),释放插入意向锁,导致事务2,3同时持有gap锁,等待insert intention锁,死锁形成!
delete的where子句没有满足条件的记录,而对于不存在的记录 并且在RR级别下,delete加锁类型为gap lock,gap lock之间是兼容的,所以两个事务都能成功执行delete;关于gap lock可以参考文章加锁分析。这里的gap范围是索引a列(3,5)的范围。
insert时,其加锁过程为先在插入间隙上获取插入意向锁,插入数据后再获取插入行上的排它锁。又由于插入意向锁与gap lock和 Next-key lock冲突,即一个事务想要获取插入意向锁,如果有其他事务已经加了gap lock或 Next-key lock,则会阻塞。
场景中两个事务都持有gap lock,然后又申请插入意向锁,此时都被阻塞,循环等待造成死锁。