41

所有关于 SQL Server 死锁的文档都讨论了操作 1 锁定资源 A 然后尝试访问资源 B 并且操作 2 锁定资源 B 并尝试访问资源 A 的场景。

然而,在我们的一些繁忙的应用程序中,我经常看到选择和更新之间甚至是多个选择之间的死锁。我发现死锁跟踪输出的一些更好的点非常难以理解,但我真的很想了解什么会导致两个单一操作之间的死锁。当然,如果选择具有读锁,则更新应该在获得排他锁之前等待,反之亦然?

这发生在 SQL Server 2005 上,我认为这并不重要。

4

5 回答 5

23

发生这种情况是因为选择锁定了两个不同的索引,同时更新以相反的顺序锁定了相同的索引。select 需要两个索引,因为第一个索引没有覆盖它需要访问的所有列;更新需要两个索引,因为如果您更新索引的键列,您需要锁定它。

http://blogs.msdn.com/bartd/archive/2006/09/25/770928.aspx有一个很棒的解释。建议的修复包括添加一个涵盖选择需要的所有列的索引、切换到快照隔离或显式强制选择获取它通常不需要的更新锁。

于 2009-03-25T14:08:40.910 回答
14

我很惊讶没有人提到WITH (UPDLOCK)锁定提示。如果您遇到涉及例如两个并行运行的选择插入对的死锁,这将非常有用。

在 SQL Server 中,如果您使用 发出选择WITH (UPDLOCK),则第二个选择将等到第一个选择完成。否则它们会获得共享锁,并且当它们同时尝试升级到独占锁时,它们会死锁。

于 2010-06-07T22:57:53.330 回答
6

我的猜测是 select-statement aquires a read-lock,当您使用 update-statement 时,它需要升级到 write-lock。

升级到写锁需要删除所有其他读锁(它们的选择事务完成)。但是,如果另一个进程已经有了升级到写锁的绝妙主意,那么你突然有两个进程在等待对方释放读锁,因此它们可以获得写锁。

如果使用 select-for-update (UPDLOCK),那么它将从一开始就获得一个写锁,然后您就不会遇到死锁问题。

于 2010-06-18T12:00:02.093 回答
5

单个查询之间的锁定可能发生在它们锁定单个行而不是整个表时:

更新查询在表中的几行上获得更新锁,而选择查询在表中的其他一些行上获得读锁。然后,更新查询尝试在读锁定的行上获取更新锁,而选择查询尝试在更新锁定的行上获取读锁。

升级锁可能会变得更加复杂,即数据库决定有太多单行被事务锁定,因此应该升级为锁定表的一部分或整个表。这意味着锁可能会影响查询中不直接涉及的行。

于 2009-03-19T12:29:35.027 回答
1

正确阅读事务和隔离级别:对于一些密集但相当彻底和技术中立的工作,请参阅事务处理原理。它震撼了我的世界(让我头疼不已!)。

我不确定您遇到了什么问题,或者您使用的是什么隔离级别。但是考虑一下:数据库引擎都知道,如果您在一个事务中进行读取,它如何判断您以后是否要进行写入?每当读取完成时,高隔离级别都需要锁定,可能在整个表上以防止幻读,因为数据可能会影响以后的写入。

您是否希望数据库等待任意长的时间才能对您的数据进行独占锁定?看看你的隔离级别,以及你是否不必要地将一系列读取作为隔离事务运行。但是,要确定您可以忍受的肮脏阅读并不总是那么容易,但是...

于 2009-03-19T12:16:00.413 回答