记一次mysql死锁原因分析和解决方式

问题代码:

记一次mysql死锁原因分析和解决方式_第1张图片

 现象:当多线程调用该接口时,产生了死锁问题。

mysql死锁日志:

记一次mysql死锁原因分析和解决方式_第2张图片

 本地复现控制台日志:

死锁

死锁(Deadlock)  所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。

分析过程

1. 由于mysql死锁日志提到了 business_id(表字段),然后就去查看表结构及索引,发现business_id,businees_type,deal_id 三个字段是一个联合唯一索引。但是查看日志中的insrt语句发现business_id不同,理论上不应该死锁问题。于是就开始各种查询资料,并没有找到相关的合理解释(失败告终)。

2.于是又开始从mysql死锁日志分析,恶补mysql锁。

死锁日志分析:

1、事务1执行insert语句等待获得X锁;

2、事务2现持有S锁,但执行insert语句也在等待X锁,这样就存在两个事务间相互等待,死锁产生,Mysql自动回滚了事务2;

3、表引擎为innodb,行锁,在字段business_id形成,普通索引而非主键索引导致顺序紊乱;

4、因为Mysql死锁日志打印不完全,无法知道上文死锁产生前的sql语句的执行情况,根据以上还无法完全分析出死锁产生的根本原因。

mysql锁粒度

共享锁Shared Locks(S锁)
1、兼容性:加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁

2、加锁方式:select…lock in share mode

排他锁Exclusive Locks(X锁)
1、兼容性:加了X锁的记录,不允许其他事务再加S锁或者X锁

2、加锁方式:select…for update

表锁:意向锁 Intention Locks,意向锁相互兼容
1、表明“某个事务正在某些行持有了锁、或该事务准备去持有锁”

2、意向锁的存在是为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存,。

3、例子:事务A修改user表的记录r,会给记录r上一把行级的排他锁(X),同时会给user表上一把意向排他锁(IX),这时事务B要给user表上一个表级的排他锁就会被阻塞。意向锁通过这种方式实现了行锁和表锁共存且满足事务隔离性的要求。

行锁:记录锁(Record Locks)
(1)记录锁, 仅仅锁住索引记录的一行,在单条索引记录上加锁。
(2)record lock锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。
所以说当一条sql没有走任何索引时,那么将会在每一条聚合索引后面加X锁,这个类似于表锁,但原理上和表锁应该是完全不同的。

行锁:间隙锁(Gap Locks)
(1)区间锁, 仅仅锁住一个索引区间(开区间,不包括双端端点)。
(2)在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录本身。

比如在 1、2、3中,间隙锁的可能值有 (∞, 1),(1, 2),(2, ∞),
(3)间隙锁可用于防止幻读,保证索引间的不会被插入数据

行锁:临键锁(Next-Key Locks)
(1)record lock + gap lock, 左开右闭区间。
(2)默认情况下,innodb使用next-key locks来锁定记录。select … for update
(3)但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。
(4)Next-Key Lock在不同的场景中会退化:

最终原因分析:

1、innodb引擎下update在默认情况下是行锁,但是在Mysql默认隔离级别(可重复读)下,一旦update更新的数据行不存在,则会产生间隙锁(Gap lock)

2、事务1 update不存在的数据行,产生了Gap lock,事务2 update不存在的数据行,也产生了Gap lock;

3、事务1 insert操作需要等待对方释放X锁,事务2 insert操作也需要等待对方释放X锁,死锁产生,Mysql自动回滚了事务2;

如何解决死锁:

解决死锁的原则就是破坏死锁产生的条件,在以上案例中,只需要判断当对应数据行不存在时,不执行update语句即可。于是将现有接口才分成save和update。

你可能感兴趣的:(mysql,数据库,sql)