死锁的发生与否,并不在于事务中有多少条SQL语句,死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。分析MySQL每条SQL语句的加锁规则,分析出每条语句的加锁顺序,然后检查多个并发SQL间是否存在以相反的顺序加锁的情况,就可以分析出各种潜在的死锁情况,也可以分析出线上死锁发生的原因。
死锁情况
加锁顺序不一致
不同索引下,加锁顺序不一致,比较隐晦
虽然每个Session都只有一条语句,仍旧会产生死锁。要分析这个死锁,首先必须用到本文前面提到的MySQL加锁的规则。针对Session 1,从name索引出发,读到的[hdc, 1],[hdc, 6]均满足条件,不仅会加name索引上的记录X锁,而且会加聚簇索引上的记录X锁,加锁顺序为先[1,hdc,100],后[6,hdc,10]。而Session 2,从pubtime索引出发,[10,6],[100,1]均满足过滤条件,同样也会加聚簇索引上的记录X锁,加锁顺序为[6,hdc,10],后[1,hdc,100]。发现没有,跟Session 1的加锁顺序正好相反,如果两个Session恰好都持有了第一把锁,请求加第二把锁,死锁就发生了.
RC下,因走全表锁表情况
- session2 在insert时,锁id10。
- session1 在delete的时候,走全表扫描,会先对所有行先加锁,在server层判断不符合条件,在进行解锁,在对Id10加锁时,发现之前已经有锁,锁等待
- session2 和2步骤类型,在对Id1加锁时,发现锁等待。
- session1和session2互相等待对方完成,死锁产生
解除死锁
- 查看:show engine innodb status \G;
- 查看是否锁表,查看进程,杀死进程
- 查看当前锁定和等待的事务,杀死进程
如何避免
- 加锁顺序一致,尽可能一次锁定所需数据行
- 保持简短的事务,单次操作数量不宜过多
- 使用较低的隔离级别
- 合理使用索引,减少不必要的索引
- 通过参数innodb_lock_wait_timeout锁等待超时时间,根据实际业务场景来设置超时时间,InnoDB引擎默认值是50s。
- 发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑(默认是开启状态)