一、前言
这一篇是死锁记录的第四篇,记录了一个三个会话并发情况下形成的死锁
如果没有特别说明,隔离级别均为RR
二、死锁输出
2018-03-26 13:34:48 0x7f9a34407700
*** (1) TRANSACTION:
TRANSACTION 3055184, ACTIVE 3180 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 98706, OS thread handle 140299540100864, query id 3302239 127.0.0.1 root updating
update tu set c3=5 where c1=4
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 240 page no 5 n bits 72 index uniq_c1 of table `db01`.`tu` trx id 3055184 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 3055185, ACTIVE 3176 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 98707, OS thread handle 140300278331136, query id 3302240 127.0.0.1 root updating
update tu set c3=5 where c1=4
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 240 page no 5 n bits 72 index uniq_c1 of table `db01`.`tu` trx id 3055185 lock mode S
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 240 page no 5 n bits 72 index uniq_c1 of table `db01`.`tu` trx id 3055185 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (2)
表中的记录
id | c1 | c2 | c3 |
---|---|---|---|
33 | 1 | 0 | 2 |
53 | 3 | 3 | 2 |
其中
id
为主键,c1
和c2
都是唯一索引,UNIQUE KEY uniq_c1 (c1), UNIQUE KEY uniq_c2 (c2)
SQL 执行顺序
Time | Sess 1 | Sess 2 | Sess 3 |
---|---|---|---|
@t1 | begin | ||
@t2 | begin | ||
@t3 | begin | ||
@t4 | insert into tu (c1,c2,c3) values(4,4,4) | ||
@t5 | insert into tu (c1,c2,c3) values(4,4,4) | ||
@t6 | insert into tu (c1,c2,c3) values(4,4,4) | ||
@t7 | commit | ||
@t8 | update tu set c3=5 where c1=4 | ||
@t9 | update tu set c3=5 where c1=4 |
业务上的主要逻辑是首先执行插入,如果插入成功,则提交,如果插入的时候报唯一键冲突,则执行更新。上面的SQL是相当于同时三个并发在执行,
Sess1
插入成功,Sess2
和Sess3
插入失败,执行更新
死锁分析
Sess2
在@t5时刻执行插入,由于插入的时候遇到Sess1
插入的同一条记录,还没提交,唯一键冲突,因此需要在唯一键加上一个S
锁,等待Sess3
在@t6时刻执行插入,由于插入的时候遇到Sess1
插入的同一条记录,还没提交,唯一键冲突,因此同样需要在唯一键加上一个S
锁,等待Sess1
在@t7时刻执行了提交,此时Sess2
和Sess3
都同时拿到了S
锁,但是由于唯一键冲突了,插入失败Sess2
在@t8时刻执行更新,由于Sess2
和Sess3
都同时拿到了S
锁,更新需要申请记录上的X锁,和Sess3
在记录上持有的S锁冲突,发生等待Sess3
在@t9时刻执行更新,由于Sess2
和Sess3
都同时拿到了S
锁,更新需要申请记录上的X
锁,和Sess2
在记录上持有的S锁冲突,形成了死锁条件。
这个死锁的根本原因是因为插入的时候遇到唯一键冲突的情况下,会申请一个
S
锁,然后执行报错的情况下,依然持有
三、小结
本案例的解法改成insert on duplicate key 写法