最近我们的一个 ASP.NET 应用程序显示了一个数据库死锁错误,我被要求检查并修复该错误。我设法找到死锁的原因是一个存储过程,它严格更新游标中的表。
这是我第一次看到这个错误并且不知道如何有效地跟踪和修复它。我尝试了所有可能的方法,最后发现正在更新的表没有主键!幸运的是,这是一个身份列。
后来我发现为部署编写数据库脚本的开发人员搞砸了。我添加了一个主键,问题就解决了。
我感到很高兴并回到我的项目中,并进行了一些研究以找出导致僵局的原因......
显然,这是导致死锁的循环等待条件。没有主键的更新显然比使用主键需要更长的时间。
我知道这不是一个明确的结论,这就是我在这里发帖的原因......
跟踪死锁是两者中比较容易的:
默认情况下,死锁不会写入错误日志。您可以使用跟踪标志 1204 和 3605 导致 SQL 将死锁写入错误日志。 将死锁信息写入 SQL Server 错误日志:DBCC TRACEON(-1, 1204, 3605) 将其关闭:DBCC TRACEOFF(-1, 1204, 3605) 有关跟踪标志 1204 的讨论以及打开它时您将获得的输出,请参阅“故障排除死锁”。 https://msdn.microsoft.com/en-us/library/ms178104.aspx
默认情况下,死锁不会写入错误日志。您可以使用跟踪标志 1204 和 3605 导致 SQL 将死锁写入错误日志。
将死锁信息写入 SQL Server 错误日志:DBCC TRACEON(-1, 1204, 3605)
将其关闭:DBCC TRACEOFF(-1, 1204, 3605)
有关跟踪标志 1204 的讨论以及打开它时您将获得的输出,请参阅“故障排除死锁”。 https://msdn.microsoft.com/en-us/library/ms178104.aspx
预防比较困难,基本上你必须注意以下几点:
代码块 1 按顺序锁定资源 A,然后锁定资源 B。
代码块 2 依次锁定资源 B,然后是资源 A。
这是可能发生死锁的典型情况,如果两个资源的锁定都不是原子的,则代码块 1 可以锁定 A 并被抢占,然后代码块 2 在 A 获得处理时间之前锁定 B。现在你有僵局。
为了防止这种情况,您可以执行以下操作
代码块 A(伪代码)
Lock Shared Resource Z Lock Resource A Lock Resource B Unlock Shared Resource Z ...
代码块 B(伪代码)
Lock Shared Resource Z Lock Resource B Lock Resource A Unlock Shared Resource Z ...
完成后不要忘记解锁 A 和 B
这将防止代码块 A 和代码块 B 之间的死锁
从数据库的角度来看,我不确定如何防止这种情况发生,因为锁是由数据库本身处理的,即更新数据时的行/表锁。我看到的问题发生最多的地方是您在光标内看到的问题。游标是出了名的低效,尽可能避免使用它们。