我对《Mysql死锁排查:insert on duplicate死锁一次排查分析过程 - 少说点话 - 博客园》的读后感
原文在这里:
Mysql死锁排查:insert on duplicate死锁一次排查分析过程
比较菜,看了一遍还是不懂死锁是怎么形成的。绕了很多圈才全理解。特此记录。
关于mysql版本
我先用5.7.34尝试复现,但是没复现。换了和原本一样的5.7.21成功复现。
肯定是某个版本修复了相关问题,但是翻了翻mysql 官方的changelog没看见相关记录😅
补充“insert on duplicate key加锁验证”小节
原本使用show engine innodb status查看锁请求信息。这里需要主要提前打开相关设置才能看见,不然看不见
set GLOBAL innodb_status_output_locks=ON;
set GLOBAL innodb_status_output=ON;
另外补充查看mysql锁情况的语句,也可以辅助分析
/*查看正在运行的事务*/
select * from information_schema.innodb_trx;
/*查看当前的锁信息*/
select * from information_schema.innodb_locks;
/*查看锁等待的信息*/
select * from information_schema.innodb_lock_waits;
/*查看innodb信息,包括事务锁信息*/
show engine innodb status;
我先按照原文,执行事务1和事务2,复现的结果和原文一样。
然后我进一步,执行事务1和事务2再加事务3,尝试换个角度理解。结果如下:
show engine innodb status;
结果没放上来,就看这些吧。插入意向锁这里看不出来,就看gap锁吧。
结论:
- 每个事务,就是每个insert duplicate语句,都加了gap锁
- 事务3,就是最晚的编号1858的事务,目前是在等事务1和事务2的gap锁。
重新整理事务1事务2事务3的执行过程中的锁情况,解释死锁的发生:
需要主要的点:
- 每个事务都是对(10,20)区间加gap锁,是都去加锁,而不是(10,20)区间上加一个锁然后各个事务获取这个锁。
- 同一个事务不受锁兼容表格的限制。
- insert duplicate就是先加间隙锁,再加插入意向锁(这本身也是一个知识点)。
-
插入意向锁会被其他的事务的间隙锁阻塞,但是不会被自己的间隙锁阻塞
|
事务1
|
事务2
|
事务3
|
步骤1
|
begin
|
|
|
步骤2
|
insert into song_rank(songId,weight) values(15,100) on duplicate key update weight=weight+1; (Query OK, 1 row affected (0.00 sec) )
|
|
|
步骤3
|
|
begin
|
|
步骤4
|
|
insert into song_rank(songId,weight) values(16,100) on duplicate key update weight=weight+1; //被阻塞
|
|
步骤5
|
|
|
begin
|
步骤6
|
|
|
insert into song_rank(songId,weight) values(18,100) on duplicate key update weight=weight+1; //被阻塞
要等待2个gap锁,被2个gap锁阻塞
|
步骤7
|
rollback
|
|
|
步骤8
|
|
Query OK, 1 row affected (40.83 sec)
前面一rollback
间隙锁A就没了,就要重新计算锁的情况。
这时候再仔细一看,虽然A没了,但是还有E锁。
事务3,又再等C锁。
这种情况就是:事务2持有C等待E,事务3持有E等待C,循环等待形成死锁
|
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
|