场景
数据表结构如下:
|
|
其中有两个索引:
- 唯一键自增索引id。
- 联合唯一索引 (source_type,source_id)。
当并发使用insert on duplicate key update 插入数据时候会必现死锁。具体的sql如下(其中source_id的值不同):
|
|
出现死锁后使用show engine innodb status;查看死锁日志如下:
大致的信息:
- transaction1尝试获取一个insert intension lock等待。
- transaction2持有一个gap lock,等待一个insert intension lock。
- 最后transaction1回滚。
复现
尝试复现这个死锁,步骤如下:
transaction1:
transaction2:
No | transaction1 | transaction2 |
---|---|---|
1 | begin | |
2 | begin | |
3 | sql1 | |
4 | sql2(block) | |
5 | sql3 | |
6 | DeadLock Error | |
7 | commit | |
8 | commit |
分析
在执行完4还未执行5之后看看两个transaction持有和等待锁的情况:
案例中的Transaction和复现步骤中的Transaction中对应关系如下:
- Transaction9036:Transaction2
- Transaction9034:Transaction1
锁的持有及等待锁情况:
Transaction1执行完sql1后
持有:
- IX锁(表锁)
- gap X锁, uniq index在50之前的锁
- record X锁,uniq index在41上的锁
- gap X锁, uniq index在41之前的锁
等待: 无
Transaction2执行完sql2后
持有:
- IX锁(表锁)
- gap X锁, uniq index在41之前的锁
等待:
- insert intention lock(40)等待Transaction1的gap X锁(41之前)
Transaction1执行sql3
等待:
- insert intention lock(38)等待Transaction1的gap X锁(41之前)