我对《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锁吧。

结论:

  1. 每个事务,就是每个insert duplicate语句,都加了gap锁
  2. 事务3,就是最晚的编号1858的事务,目前是在等事务1和事务2的gap锁。

 

重新整理事务1事务2事务3的执行过程中的锁情况,解释死锁的发生:

需要主要的点:

  1. 每个事务都是对(10,20)区间加gap锁,是都去加锁,而不是(10,20)区间上加一个锁然后各个事务获取这个锁。
  2. 同一个事务不受锁兼容表格的限制。
    1. insert duplicate就是先加间隙锁,再加插入意向锁(这本身也是一个知识点)。
    2. 插入意向锁会被其他的事务的间隙锁阻塞,但是不会被自己的间隙锁阻塞

 

 

 
事务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
 

 

posted @ 2022-12-26 16:15  董客园  阅读(155)  评论(0编辑  收藏  举报