线上 MySql 事务死锁,应该怎么排查解决?( 二 )


 

线上 MySql 事务死锁,应该怎么排查解决?

文章插图
【线上 MySql 事务死锁,应该怎么排查解决?】 

线上 MySql 事务死锁,应该怎么排查解决?

文章插图
 
下面是 MySql 的其他锁
意向锁
InnoDB为了支持多粒度(表锁和行锁)的锁并存,引入意向锁 。意向锁是表级锁,分为IS锁和IX锁 。
  • 意向共享锁(IS)事务在请求S锁前,需要先获得对应的IS锁
  • 意向排他锁 (IX)事务在请求X锁前,需要先获得对应的IX锁
锁兼容矩阵
线上 MySql 事务死锁,应该怎么排查解决?

文章插图
 
自增锁 auto-inc lock
AUTO-INC锁是事务中的一种特殊的表级锁,通过AUTO_INCREMENT的列来实现,这种锁是作用于语句的而不是事务 。
记录锁 record Lock
即行锁 。单条索引记录上加锁,record lock锁住的永远是索引,而非记录本身 。
间隙锁 gap lock
区间锁, 仅仅锁住一个索引区间(开区间) 。在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身 。GAP锁的目的是为了防止同一事务的两次当前读,出现幻读的情况 。
临键锁 next key lock
行锁和间隙锁组合起来就叫Next-Key-Lock,左开右闭区间 。默认情况下,innodb使用next-key locks来锁定记录 。但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围 。
插入意向锁 insert intention lock
Gap Lock中存在一种插入意向锁(Insert Intention Lock),在insert操作时产生 。在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待 。假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突 。
注:插入意向锁并非意向锁,而是一种特殊的间隙锁 。
如果插入前,该间隙已经有gap锁,那么insert 会申请插入意向锁 。因为了避免幻读,当其他事务持有该间隙的间隔锁,插入意向锁就会被阻塞(不用直接用gap锁,是因为gap锁不互斥) 。
  • innodb_lock_waits 被阻塞的锁记录
这张表里有记录就说明有事务被阻塞里 。
线上 MySql 事务死锁,应该怎么排查解决?

文章插图
 
主要字段含义
线上 MySql 事务死锁,应该怎么排查解决?

文章插图
 
03. 解决方案及总结线上遇到死锁怎么解决?最快的方式当然是 kill 事务,重启服务,根本原因还是需要看这三张表,以后再遇到数据库死锁、事务死锁,查这三张表就差不多知道原因了 。
我们该如何避免死锁呢?常规的回答都是以固定的顺序访问数据 。但本案例是因为使用了 REQUIRES_NEW 导致 。
使用 REQUIRES_NEW 的原因以下场景,内层事务是一个批量更新,但是又不希望因为某一条失败而影响其他的更新 。
begin @t1aMApper.update()for pojo in pojos:begin @t2  bMapper.update(pojo)  rpc.update()  commitcommit所以一定要避免内外双层事务修改同一条数据的情况,对于 Spring 事务传播机制也要熟知其作用 。
要保证数据的最终一致性,应该写成一个Job,更新失败后不断的去补偿 。
公众号:看起来很美(kanqilaihenmei_)




推荐阅读