什么是数据库中的死锁?

30

什么是SQL Server中的死锁,它是何时出现的?

死锁可能存在哪些问题以及如何解决呢?

5个回答

49

通常,死锁是指两个或多个实体阻塞了某些资源,并且它们都无法完成,因为它们以循环方式阻塞资源。

举个例子:假设我有表A和表B,我需要先在A中进行一些更新,然后在B中进行更新,并决定在使用时同时锁定它们(这是非常愚蠢的行为,但现在它能够说明问题)。与此同时,另一个人以相反的顺序做了同样的事情 - 首先锁定了B,然后锁定了A。

按时间顺序发生的事情如下:

proc1: 锁定 A

proc2: 锁定 B

proc1: 锁定 B - 开始等待,直到proc2释放B

proc2: 锁定 A - 开始等待,直到proc1释放A

他们中没有一个会完成。这就是死锁。在实践中,这通常会导致超时错误,因为不希望任何查询永远挂起,并且底层系统(例如数据库)将终止未能及时完成的查询。

死锁的一个真实例子是当你把房门钥匙锁在车里,而把车钥匙锁在房子里时。


@Pz,我有一个疑问。NOLOCK提示会向数据磁盘写入吗?我的意思是它会提交事务还是强制放弃事务以允许对另一个的访问? - VenkatReddy.Ravu
Nolock的作用就是确保查询不会生成任何锁。仅此而已。它不会影响除此之外的任何其他内容。 - TomTom
很有趣,因为我谷歌搜索了这个问题,它就是第一个结果 :) - pglynn
1
一个真实的死锁例子是当你把房子的钥匙锁在了车里,而把车钥匙锁在了房子里。这个例子很令人困惑,暗示着只有一个参与者陷入了死锁,而实际上至少有两个。 - Yassin Hajaj

29

死锁是什么

死锁发生在两个并发事务互相等待对方释放锁的情况下,如下图所示。

Deadlock

由于两个事务都处于锁获取阶段,因此在获取下一个锁之前,它们都不会释放任何锁。

从死锁中恢复

如果您使用依赖于锁的并发控制算法,则始终存在运行进入死锁状态的风险。死锁可以在任何并发环境中发生,而不仅仅是在数据库系统中。

例如,如果两个或多个线程正在等待先前获取的锁,以至于没有线程可以取得任何进展,则多线程程序可能会死锁。如果这在Java应用程序中发生,则JVM不能强制一个线程停止执行并释放其锁。

即使 Thread 类公开了 stop 方法,但此方法自Java 1.1版本以来已被弃用,因为它可能会导致线程停止后对象处于不一致状态。相反,Java定义了一个interrupt方法,作为线程的提示,被中断的线程可以简单地忽略中断并继续执行。
正因为这个原因,Java应用程序无法从死锁情况中恢复,而应用程序开发人员有责任按照某种方式对锁获取请求进行排序,以便死锁永远不会发生。
然而,数据库系统无法强制执行给定的锁获取顺序,因为无法预见某个事务将要进一步获取哪些其他锁。保持锁定顺序成为数据访问层的责任,数据库只能帮助恢复死锁情况。
数据库引擎运行一个单独的进程,扫描当前冲突图以查找锁等待循环(由死锁引起)。当检测到循环时,数据库引擎选择一个事务并将其中止,导致其锁被释放,以便另一个事务可以继续。

与JVM不同,数据库事务被设计为原子工作单元。因此,回滚会使数据库处于一致状态。

死锁优先级

当数据库选择回滚其中一个被卡住的事务时,并不总是能够预测哪一个事务将被回滚。通常情况下,数据库可能会选择回滚回滚成本较低的事务。

Oracle

根据Oracle文档所述,检测到死锁的事务将会被回滚。

SQL Server

SQL Server允许您通过DEADLOCK_PRIORITY会话变量控制在死锁情况下哪个事务更有可能被回滚。

DEADLOCK_PRIORITY会话可以接受从-10到10的任何整数或预定义值,例如LOW(-5)NORMAL(0)HIGH(5)

在死锁情况下,当前事务将回滚,除非其他事务具有更低的死锁优先级值。如果两个事务具有相同的优先级值,则SQL Server将回滚回滚成本较小的事务。

PostgreSQL

文档所述,PostgreSQL不保证将回滚哪个事务。

MySQL

MySQL试图回滚修改记录数量最少的事务,因为释放较少的锁定成本更低。 (来源)

这个答案比已接受的更具体。我更愿意把这个放在最上面。 - Yassin Hajaj

10
死锁是指当两个人需要多个资源来执行时,其中一些资源被每个人锁定时发生的情况。这导致A没有B所需的东西就无法执行,反之亦然。
比如说我有人A和人B。他们都需要获取两行才能运行(Row1和Row2)。
- 人A锁定Row1并尝试获取Row2。 - 人B锁定Row2并尝试获取Row1。
由于他们锁定了对方所需的资源,A无法运行因为它需要Row2,B也无法运行因为它需要Row1。两个人都永远无法执行。
减少死锁的一个相对简单的方法是,在所有复杂的事务中,按照相同的顺序进行操作。换句话说,先访问Table1,然后按相同的顺序访问Table2。这将有助于减少发生死锁的次数。

1
当两个或多个事务正在等待由对方持有的锁被释放时,可能会发生僵局。

1
死锁是指进程或线程因为所请求的系统资源被另一个等待中的进程持有而进入等待状态。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接