Mysql死锁如何排查?insert on duplicate死锁排查过程分析( 三 )


【Mysql死锁如何排查?insert on duplicate死锁排查过程分析】通过分析死锁日志,我们可以找到发生死锁的SQL,以及相关等待的锁,我们再对对应的SQL进行加锁分析,其实问题就迎刃而解了 。
OK,我们回到对应的SQL,insert into song_rank(songId,weight) values(16,100) on duplicate key update weight=weight+1 执行过程到底加了什么锁呢?
① insert加锁策略
insert语句会对插入的这条记录加排他记录锁,在加记录锁之前还会加一种 GAP 锁,叫做插入意向(insert intention)锁,如果出现唯一键冲突,还会加一个共享记录(S)锁 。
(SQL加锁分析非常重要,在这里给大家推荐一篇文章,讲的非常好,解决死锁之路 - 常见 SQL 语句的加锁分析)
② insert on duplicate key加锁验证
为了验证一下insert on duplicate key加锁情况,我们拿上面demo的事务1和2在走一下流程 。
事务1:
mysql> begin; //第一步Query OK, 0 rows affected (0.00 sec)mysql> insert into song_rank(songId,weight) values(15,100) on duplicate keyupdate weight=weight+1; //第二步Query OK, 1 row affected (0.00 sec)事务2(另开窗口):
mysql> begin; //第三步Query OK, 0 rows affected (0.00 sec)mysql> insert into song_rank(songId,weight) values(16,100) on duplicate key update weight=weight+1; // 第四步使用show engine innodb status查看当前锁请求信息,如图:

Mysql死锁如何排查?insert on duplicate死锁排查过程分析

文章插图
 
事务2持有:IX锁(表锁),gap x锁,insert intention lock(在等待事务1的gap锁)
所以,insert on duplicate 执行过程会上这三把锁 。
(5)死锁原因分析
回归到本文开头介绍的死锁案发模拟现场(事务1,2,3)以及死锁日志现场,
案发后事务1的锁:
Mysql死锁如何排查?insert on duplicate死锁排查过程分析

文章插图
 
案发后事务2的锁:
Mysql死锁如何排查?insert on duplicate死锁排查过程分析

文章插图
 
案发复原路线:
首先,执行事务1执行: begin; insert into song_rank(songId,weight) values(15,100) on duplicate key update weight=weight+1; 会获得 gap锁(10,20),insert intention lock(插入意向锁)
接着,事务2执行: begin; insert into song_rank(songId,weight) values(16,100) on duplicate key update weight=weight+1; 会获得 gap锁(10,20),同时等待事务1的insert intention lock(插入意向锁) 。
再然后,事务3执行: begin; insert into song_rank(songId,weight) values(18,100) on duplicate key update weight=weight+1; 会获得 gap锁(10,20),同时等待事务1的insert intention lock(插入意向锁) 。
最后,事务1回滚(rollback),释放插入意向锁,导致事务2,3同时持有gap锁,等待insert intention锁,死锁形成!
锁模式兼容矩阵(横向是已持有锁,纵向是正在请求的锁):
兼容性 Gap Insert Intention Record Next-Key Gap 兼容 兼容 兼容 兼容 Insert Intention 冲突 兼容 兼容 冲突 Record 兼容 兼容 冲突 冲突 Next-Key 兼容 兼容 冲突 冲突 这是MySql5.7的一个bug
04 如何避免该insert on duplicate死锁问题4.1 把insert on duplicate改为insert
try{ insert(); }catch(DuplicateKeyException e){ update(); }因为insert不会加gap锁,所以可以避免该问题 。
4.2 更改MySql版本
既然这是MySql5.7的一个bug,那么可以考虑更改Mysql版本 。
4.3 尽量减少使用unique index
gap锁跟索引有关,并且unique key 和foreign key会引起额外的index检查,需要更大的开销,所以我们尽量减少使用不必要的索引 。
05 本文总结(重要)本文介绍了MySql5.7死锁的一个bug 。我们应该怎样去排查死锁问题呢?
  1. show engine innodb status;查看死锁日志
  2. 找出死锁SQL
  3. SQL加锁分析
  4. 分析死锁日志(持有什么锁,等待什么锁)
  5. 熟悉锁模式兼容矩阵,InnoDB存储引擎中锁的兼容性矩阵




推荐阅读