MySQL-16丨死锁

Posted by jiefang on October 28, 2019

死锁

死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

解决死锁

InnoDB 中解决死锁问题有两种方式:

  • 检测到死锁的循环依赖,立即返回一个错误(这个报错内容请看下面的实验),将参数innodb_deadlock_detect 设置为 on 表示开启这个逻辑;
  • 等查询的时间达到锁等待超时的设定后放弃锁请求。这个超时时间由 innodb_lock_wait_timeout 来控制。默认是 50 秒。

产生死锁原因

同一张表中

不同线程并发访问同一张表的多行数据,未按顺序访问导致死锁。

image

session1 在等待 session2 释放 a=2 的行锁,而 session2 在等待 session1 释放 a=1 的行锁。两个 session 互相等待对方释放资源,就进入了死锁状态。

所以对于程序多个并发访问同一张表时,如果事先确保每个线程按固定顺序来处理记录,可以降低死锁的概率。

不同表之间

image 不同程序并发访问多个表时,应尽量约定以相同的顺序来访问表,可大大降低并发操作不同表时死锁发生的概率。

事务隔离级别

RR 隔离级别下,由于间隙锁导致死锁: image

可以知道 SQL3 需要等待 a=2 获得的间隙锁,而 SQL4 需要等待 a=1 获得的间隙锁,两个 session 互相等待对方释放资源,就进入了死锁状态。

降低死锁

  • 更新 SQL 的 where 条件尽量用索引;
  • 基于 primary 或 unique key 更新数据;
  • 减少范围更新,尤其非主键、非唯一索引上的范围更新;
  • 加锁顺序一致,尽可能一次性锁定所有需要行;
  • 将 RR 隔离级别调整为 RC 隔离级别。