长时间运行的 SELECT 查询失败,因为 SQL SERVER 上的 DELETE 导致死锁受害者 - 为什么,以及如何避免这种情况?

sar*_*off 1 sql-server deadlock parallelism transaction

首先 1 个线程从表中选择大量数据。

然后,在 SELECT 查询尚未完成之前,另一个线程从该表中删除数据。

它导致 SELECT 查询失败,错误 errorCode=1205 sqlState=40001 和发生死锁的消息。

我认为第二个事务必须等到第一个事务完成,或者第一个事务必须在没有第二个事务所做更改的情况下返回数据。我怎样才能避免这种情况?

我无法改变交易的顺序或以某种方式控制它们(应用程序的用户很多,他们可以随时以任何顺序做任何他们想做的事情)。为什么会发生?

查询是这样的(我对它们进行了一些混淆):

SELECT distinct P.pID, (LastNAME+' '+FirstName+' '+SurName) as NAME, 'person' as Type, CA.OrgName, PC.personemail as Email
FROM Persons P
INNER JOIN WORK PW ON (P.mainwork = PW.pwId)
INNER JOIN CONTACT PC ON (PC.pID = P.pID)
LEFT OUTER JOIN ORG CA ON (CA.orgId = PW.orgId)
WHERE (P.Status = ?)
Run Code Online (Sandbox Code Playgroud)

DELETE FROM persons WHERE id = 1234
Run Code Online (Sandbox Code Playgroud)

Ian*_*n_H 6

我认为第二笔交易必须等到第一笔交易完成

基本上,这不是真的。如果一次只有一个人可以用它做某事,那么您(或任何人)的数据库将不会非常有效。

为什么会发生?

简单地:

SELECT 查询在它所查看的任何行上都使用共享锁,因此尽管它不会与另一个 SELECT 查询发生冲突,但它肯定会与来自 DELETE 的排他锁发生冲突。由于 SELECT 查询不会更新任何内容,因此它通常会成为死锁的受害者,因为它可能是回滚成本最低的。

如果没有进一步的信息,就不可能说在你所谈论的情况下为什么会发生这种情况。我会在最后添加一些死锁检测链接,但我怀疑这不是问题的主要目的,所以......

该怎么办?

很难知道哪种方法最适合您(没有更多信息)。您可能应该查看一下您的隔离级别。

您可以使用 READ_COMMITTED_SNAPSHOT,但这可能会导致进一步的开销,或者使用 READ_UNCOMMITED/使用 NOLOCK 提示 - 我通常不喜欢这些选项,因为返回的数据实际上可能不是您想要的,但它们可能适用于你的情况。很难确切地说什么是最好的,因为它取决于有关您的系统及其用途的许多不同变量。

在这方面你可以做的最好的事情是阅读隔离级别,这些是一些类似的帖子/来源,可以让你开始并更详细地讨论事情。

MSDN - 选择语句如何导致被选为死锁受害者?, SQL Server 中心, MSDN - 了解隔离级别

但...

不要急于进行更改,只是为了完成查询。更合适的探索途径可能是您的查询本身。

为什么 SELECT 语句需要这么长时间才能运行?为什么从表中删除的东西如此频繁?也许一些简单的查询调整会让它们更快地完成并减少死锁的机会。也许您需要对您的数据库服务器进行一些常规的性能调整(我不会在这里详细讨论这个问题,因为它已经偏离主题了)。

您必须确保您的查询适合业务目的,而不是查询只是“不惜一切代价完成”,数据库首先是一个业务工具,需要解决这个问题(无论我们有多不喜欢)!

只是继续更改隔离级别,甚至只是使用 NOLOCK,都会对您的数据库以及查询返回的确切结果产生重大影响 - 这可能会对业务产生影响。

还...

为了帮助检测死锁,请查看StackOverflow上的这篇文章并查看MSDN 站点上的死锁信息工具,该工具将解释如何使用跟踪标志来提供有关死锁的更多信息。

最后...

如果您将 SQL Server 版本放在您的帖子中,则可以提供更具体的建议,以及有关 SELECT 查询究竟是什么的更多详细信息(查看是否有任何可能导致它花费这么长时间并因锁定而打开) .