给定两个已知语句,如何复制死锁?

MT.*_*MT. 6 sql-server deadlock sql-server-2008-r2

我有两个语句(对一个表(称为表 A)中的每一行的更新和在另一个表上查找表 A 中的行的删除),我知道这会导致偶尔的死锁。好像在表A的同一个主键索引上有一个X锁和一个U锁。

我一直在尝试复制 SQL Server Management Studio 中的死锁,但失败了。我应该可以吗?

另外,delete 语句效率非常低,我我可以通过创建覆盖索引来解决这个问题,这意味着上面提到的主键索引不再包含在 delete 语句的实际执行计划中。鉴于最终两个语句都需要相同的行,这是否可以保证没有死锁,或者只是通过为 SQL Server 提供不同的数据路径来减少发生死锁的机会?

我终于设法得到一个死锁图,如下所示:

死锁图

如果我没看错,右边的流程节点在 PK_LoanFacility 上有一个排他锁,并且正在请求另一个(也许这是因为在与这个流程节点关联的语句中有两个更新。第一个将一个字段设置为 NULL所有行,然后第二行使用从另一个数据库中提取的值更新子集)。左侧的流程节点在 PK_LoanFacility 上有一个更新锁,并且正在请求一个共享锁。左边的语句从子表中删除一行,并通过 WHERE 子句查找父 ID。即 DELETE FROM table where ForeignID = (SELECT ID FROM ParentTable WHERE x = y)。我不确定为什么这需要对 PK_LoanFacility(父表的 PK)进行更新锁定。我猜每当你从表中删除一行时,

如前所述,我相当确定我可以通过添加一些适当的索引来消除对所请求的共享锁的需要,但我仍然有兴趣了解正在发生的事情。

Seb*_*ine 6

没有什么可以防止 RDBMS 中的死锁。但是,您可以通过遵循一些简单的规则来大大降低发生的机会:

  1. 使用适当的索引确保您的查询得到很好的调整。
  2. 如果您必须在多个表上使用并发锁,则您的所有语句和过程都应该以相同的顺序访问这些表。
  3. 使您的交易尽可能短。

这将使您遇到死锁的可能性大大降低。但是,您无法完全避免它们,因此您的代码应该是有弹性的,并且只是自动等待一个随机时间间隔(几毫秒),然后再试一次。只有在重试三遍后仍然无法执行语句时,才会记录错误。

现在回答您的问题,即如何重现死锁:死锁有多种类型。最简单的一种是两个资源的死锁:

  1. 连接 A 锁定资源 1
  2. 连接 B 锁定资源 2
  3. 连接 A 尝试在资源 2 上获取冲突锁并且必须等待
  4. 连接 B 尝试在资源 1 上获取冲突锁并且必须等待

现在两个连接互相等待导致死锁。通过在两个连接上一次单步执行代码,可以很容易地重现这种类型的死锁。其他死锁并不总是那么容易重现,因为例如它们可能仅在内存压力情况或类似情况下发生。

获取有关死锁的更多信息的最简单方法是设置死锁图跟踪。死锁图包含许多有助于理解捕获的死锁的信息。由于您的系统(希望如此)出现死锁的情况相对较少,因此您可能需要运行跟踪一段时间。但是,如果您仅捕获该事件,则不会使用大量资源。捕获死锁图后,将其视为 XML。Profiler 给出的图形表示确实只揭示了有限的信息量,但它是一个很好的起点。