请教一个死锁的问题
发布于 7 年前 作者 dlyt 4355 次预览 最后一次回复是 7 年前 来自 问答
我在一个事务里执行了3条 sql 分别是:
select * from table where mobile = ? for update
select prizeId,count(*) as amount from table where prizeRegion = ? group by prizeRegion,prizeId for update
insert into table (mobile,prizeId,prizeName,prizeRegion) values (?,?,?,?)
空表的时候并发会发生死锁, 但是当表中有数据的时候就不会. 不知道哪里的问题?
11 回复
Innodb Gap锁了解下
空表的时候,只有一个区间,select for update锁住了这个区间,事务没有结束的话,另外事务中的insert拿不到排他锁。
@Shasharoman 第二句sql查询的是全表, 应该也只有一个区间, 但是当表中有数据的时候并不会发生死锁. 不知道跟空表的时候有什么区别?
@dlyt 不太理解你说的
查询的是全表…Gap锁涉及的区间划分,依赖表中已存在的数据,第二条SQL中
where prizeRegion = ?,如果prizeRegion是int型非唯一索引,表中没有数据的时候自然是只有一个区间(-inf, +inf),如果表中有一条值为5的数据,那么就是两个区间(-inf, 5)、(5, +inf),如果表中有两条值分别为5、10的数据,那么就存在三个区间 (-inf, 5)、(5, 10)、(10, +inf)。触发Gap锁时,锁住的是值左右的区间,比如
where prizeRegion = 5 for update,那么5左右的区间就是(-inf, 5)、(5, 10),也就这满足这两个区间的值无法获得排他锁,所以insert需要等待上锁事务释放锁。另外,对于边界值能否insert需要结合主键,我在第一个回答的里面写的闭合区间并不正确。
@Shasharoman prizeRegion 是 varchar 型非索引, 值只有一个. 所以第二条sql查询的结果是全表的数据.
@dlyt 没有索引,整张表都被锁了,你还能insert?
因为回答这个问题,我又复习了一遍这块知识,算有些收获,不知道朋友你有没有收获?
@Shasharoman 有收获的.
你说的我能理解, 我纠结的是下面 3 条 sql 在一个事务中, 并发请求的时候如果表中没有数据就会发生死锁, 但有数据就不会. 比如当表中无数据的时候,我并发请求 5 次, 只会 insert 一条记录, 其他的会报死锁错误然后回滚. 之后并发请求就不会出现这个问题. 问题在第一条 sql. 我不知道具体什么原因导致的这个现象?
@dlyt 应该是我误解了“死锁”的意思,能贴一下具体错误信息么,我也想看看究竟是啥现象以及原因?
现象就是我上面描述的那样, 原因我还在找.
报错信息: ER_LOCK_DEADLOCK: Deadlock found when trying to get lock; try restarting transaction
@dlyt 再次深入后,大概搞清楚了原因,同样是上面的x表结构,先看一下现象:
如果表中有数据,
select for update的IX锁虽然可以并存,但行锁冲突,所以才没有出现死锁现象:详细内容可以参考MySQL官方指南
@Shasharoman 大概明白了, 感谢老哥.
@Shasharoman 分析的厉害,佩服