mysql for update 死锁问题

在RR条件, id是主键,插入五条数据

id name
1 title1
2 title2
3 title3
9 title9
10 title10
session 1 session 2
begin; begin;
select * from user where id = 6 for update;
~ select * from user where id = 6 for update
insert into user 6, title6
锁等待中 insert into user 6, title6
锁等待解除 死锁,session 2的事务被回滚

我们可以发现

针对事务一:select * from user where id = 6 for update; 第一次查询,没有找到满足查询条件的记录,那么mysql会加一个gap锁
但是事务2也可以执行 select * from user where id = 6 for update;,这个时候事务1和事务2都有3-9的GAP锁,2个事务都不能执行insert id=6的记录,会报死锁异常。gap锁本身的作用是防止后续的插入操作,因此gap锁只跟插入相冲突,gap锁之间不冲突,所以这个时候会发生死锁。

我们更换SQL语句改为
SELECT * FROM user where id >5 and id <7 for update; 其余不变。 会发现两条select语句会冲突。
引用何登成的解释:
"按照原理来说,id>5 and id<7这个查询条件,在表中找不到满足条件的项,因此会对第一个不满足条件的项(id = 9)上加GAP锁,防止后续其他事务插入满足条件的记录。

而GAP锁与GAP锁是不冲突的,那么为什么两个同时执行id>5 and id<7查询的事务会冲突呢?

原因在于,MySQL Server并没有将id<7这个查询条件下降到InnoDB引擎层,因此InnoDB看到的查询,是id>5,正向扫描。读出的记录id=9,先加上next key锁(Lock X + GAP lock),然后返回给MySQL Server进行判断。
MySQL Server此时才会判断返回的记录是否满足id<7的查询条件。此处不满足,查询结束。

因此,id=9记录上,真正持有的锁是next key锁,而next key锁之间是相互冲突的,这也说明了为什么两个id>5 and id<7查询的事务会冲突的原因。

你可能感兴趣的:(mysql for update 死锁问题)