MySQL死锁分析

1. 什么是死锁

死锁是指多个事务分别持有对方需要的锁,并且都在相互等待对方释放自己需要的锁,从而形成多个事务一直等待下去的状态。

A等待B释放锁
B等待C释放锁
C等待A释放锁
事务A
事务B
事务C
2. 为什么会形成死锁
  1. 不同表记录行锁冲突:
    一个事务中多个sql分别操作不同表中的记录时与其他的事务操作的这些表的记录相同,出现循环等待锁情况。例如:事务A先删除table_1表中id=1的记录,事务B再更新table_2表中id=2的记录,事务A再更新table_2表中id=2的记录,事务B再删除table_1表中id=1的记录。
    在这里插入图片描述
  2. 相同表记录行锁冲突 :
    不同事务中同时处理一个表中相同的记录。例如:事务A处理id为(1,2,3,4)的记录,事务B处理id为(2,1,5,6)的记录。
    在这里插入图片描述
  3. 不同索引锁冲突:
    不同事务中同时处理不同的索引记录。例如:事务A在执行一个普通索引的操作时,这个普通索引对应多个聚簇索引的加锁顺序为(1,2,3),而事务B执行聚簇索引的操作时,对应的加锁顺序是(2,1,4),这样就造成了死锁的可能性。
    在这里插入图片描述
  4. gap锁冲突:
    innodb在RR级别下,不同事务中如果包含有相同的区间,则会由于生成的间隙锁的范围重叠导致死锁。例如:事务A执行聚簇索引的区间为:(1,5),事务B执行聚簇索引的区间为:(3,8)。当事务A执行到4时属于事务B的间隙锁范围,需要等待事务B释放该间隙锁,当事务B执行到4时,也属于事务A的间隙锁范围,也需要等待事务A释放该间隙锁,然后就导致这两个事务互相等待彼此释放锁,从而形成死锁。
    在这里插入图片描述
3. 产生死锁的四个必要条件
  1. 互斥条件:至少有一个资源必须处于非共享状态,即一次只能被一个进程或线程占用。
  2. 请求与保持条件:进程或线程至少需要持有一个资源,并且在等待其他资源时不释放已占有的资源。
  3. 不可剥夺条件:已分配给进程或线程的资源不能被强制性地剥夺,只能由持有资源的进程或线程主动释放。
  4. 循环等待条件:存在一个进程或线程的资源申请序列,使得每个进程或线程都在等待下一个进程或线程所持有的资源。
4. 如何处理死锁
  1. 设置等待时间(innodb_lock_wait_timeout=50s):当一个等待时间超过设置的某一阀值时,对其中一个事务进行回滚,另一个事务就能继续执行。(缺点:如果回滚的事务更新了很多行,占用了较多的undo log,那么在回滚的时候花费的时间比另外一个正常执行的事务花费的时间可能还要多,就不太合适)
  2. 发起死锁检测:innodb还提供了wait-for graph算法来主动进行死锁检测,要求数据库保存锁的信息链表和事务等待链表两部分信息,通过这两个部分信息构造出一张图,在每个事务请求锁并发生等待时都会判断是否存在回路,如果在图中检测到回路,就表明有死锁产生,这时候InnoDB存储引擎会选择回滚undo量最小的事务。
5. 如何避免发生死锁
  1. 以固定的顺序访问表和行,避免不同的事务交叉执行相同的记录。
  2. 大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
  3. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
  4. 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  5. 为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。
  6. 操作完之后立即提交事务,特别是在交互式命令行中。
  7. 使用事务,不使用 lock tables 。
  8. 最好不要用 (SELECT … FOR UPDATE or SELECT … LOCK IN SHARE MODE)。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值