fin*_*nal 5 sql-server deadlock deadlock-graph
我们的应用程序时不时地(大约每周一次)遇到死锁。
罪魁祸首似乎是带有两个选择的查询。其中之一是出于性能原因填充临时表,另一个是具有许多连接的相对复杂的选择,以返回具有许多详细信息的所有约会的列表。我看到的关于第二个选择的唯一可能特别的地方是它包含一个自连接。二选查询始终是 SQL Server 死锁事件报告的一部分。
另一个查询是对同一个表的简单 DML 查询(插入或更新),尽管这并不总是相同的 DML 查询。这两个查询都以标准READ COMMITTED
隔离方式运行,而不是在显式事务中运行。
这两个查询大致如下(我已缩短它们以进行澄清)
DECLARE @futureAppointments TABLE(clientId int, StartDate date)
INSERT INTO @futureAppointments SELECT clientId, StartDate FROM Appointments where StartDate >= @startDate
SELECT *, (SELECT COUNT(*) FROM @futureAppointments fa WHERE fa.clientId = a.clientId AND fa.StartDate > a.StartDate)
FROM Appointments a
join b on a.fk_b = b.id
join c on a.fk_c = c.id
join Appointments d on c.somefield = d.anotherfield
WHERE a.StartDate >= @startDate AND a.StartDate <= @endDate
Run Code Online (Sandbox Code Playgroud)
UPDATE Appointments SET someField = @value WHERE id = @id
Run Code Online (Sandbox Code Playgroud)
示例 2:deadlock2.xml,
示例 3:deadlock3.xml,
在这种情况下,我将如何尝试防止发生死锁?另外,有谁知道为什么第一个带有两个选择的语句会像示例 3 一样在所选表的 PK 上获得 U 锁?我不认为这很重要,但这似乎很奇怪。
查看死锁图时:
更新查询 - SPID 75
更新查询锁定(spid 75)非常简单,在键/行值上请求X(独占)锁,而该行当前被选择查询锁定。
更新查询还持有一个页面上的IX(意图排他)锁,正如在属于该页面的行上获取排他锁时所预期的那样。的IX锁与兼容IS(意向共享)由选择查询发出锁(作为结果小号上的行锁)。
请参阅:锁兼容性矩阵
选择查询 - SPID 103
The unusual part of your deadlock is that the select query (spid 103) wants a S (shared) lock on both the row & the page that has the row data (and possibly other row data). Especially since lock escalation from row to page is not possible (row to table & page to table). A previous transaction holding the lock is also ruled out.
The explanation seems to be in the double access to the dbo.Appointments
table.
Locks are taken twice and one of these table accesses will want the page lock, while the other one already aquired the rowlock.
The update fires in between these shared locks being aquired.
An example of locks requested / acquired in order
All this means that this part of the select query is your deadlock problem:
SELECT *, (SELECT COUNT(*) FROM @futureAppointments fa WHERE fa.clientId = a.clientId AND fa.StartDate > a.StartDate)
FROM Appointments a
join b on a.fk_b = b.id
join c on a.fk_c = c.id
join Appointments d on c.somefield = d.anotherfield
WHERE a.StartDate >= @startDate AND a.StartDate <= @endDate;
Run Code Online (Sandbox Code Playgroud)
How would I try to prevent deadlocks from happening in this scenario?
Reduce
To reduce the probability of the deadlock happening you could look into optimizing the select query by rewriting or adding indexes.
Remove
To remove the possibility of deadlocks between these two queries entirely you could split the self join in two parts by using a temp table, or take a shared page or table lock on the Appointments table by using WTIH(PAGLOCK)
/ WITH(TABLOCK)
. Remember that this impacts concurrency.
另外,有谁知道为什么第一个带有两个选择的语句会像示例 3 一样在所选表的 PK 上获得 U 锁?我不认为这很重要,但这似乎很奇怪。
所有者(选择查询)持有密钥上的共享锁。
一个Ù(更新)锁(从更新查询推出)是与共享锁见兼容:锁兼容性矩阵。
更新查询尝试将 U 锁转换为排它锁:
<waiter id="process24b7826bc28" mode="X" requestType="convert" />
.
使用SentryOne Plan Explorer(免费工具)打开 XML 时,信息是正确的。