死锁的概念

指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力作用,事务都将无法推进下去。

死锁的解决方案

不等待

最简单的方式是不要有等待,将等待转化为回滚,但缺点是可能对性能造成较大影响,可能有大量的事务不能执行。

超时回滚

另一种方法是超时,及如过有两个事务互相等待,设置事务的最长等待时长,达到阈值时,进行回滚。这种方式的一个缺点是如果回滚的事务所占权重比较大,事务操作更新了很多行数据,占用了较多的 redo log,这时回滚事务的时间可能比另一个事务执行的时间还要长。

等待图(wait-for graph)

这是数据库普遍采用的一种死锁主动检测机制,InnoDB 也是采用这种方式,wait-for graph 要求数据库保存以下信息:

  • 锁的信息链表
  • 事务等待链表

下面是一个例子:


t1\t2\t3\t4 四个事务分别对两行数据 row1\ row2 持有不同的锁

S: 共享锁

X: 拍他锁

一个事务要想获取一行数据的拍他锁必须等待其他事务释放对其共享锁的占用。

上面示例总生成的 wait-for graph 如下:


在每个事务请求锁时判断是否存在回路,若存在则有死锁,通常来说 InnoDB 存储引擎会选择回滚 undo 量最小的事务。

在 InnoDB 1.2 之前采用递归方式实现,之后的版本进行了优化,采用非递归方式实现。

死锁示例

下面是一个 AB-BA 死锁问题示例


大部分死锁 InnoDB 存储引擎本身可以侦测到,不需要认为敢于,发现死锁后引擎会马上回滚一个事务。