我有一个僵局,我不知道如何解决。我已经修复了来自EF端的其他一些查询,这些查询导致了相同的死锁(在sp的同一行),但是无法修改,这是一个非常基本的查询,我认为必须有一种更简便的方法来重写SP或修改索引以避免页面锁定。
三个表:
两个过程:
僵局:
当SP尝试删除空的Charges时,它在最后一步被抛出,其中ChargeItems没有记录。至此,它已经删除了所有ChargeItems,只需要删除空的Charge和工作项。
当SP尝试删除费用时,EF所运行的查询正在通过其DMC搜索工作项。
SELECT
[Limit1].[Id] AS [Id],
[Limit1].[DMC] AS [DMC],
[Limit1].[FirstSeen] AS [FirstSeen],
[Limit1].[DrawingNo] AS [DrawingNo],
[Limit1].[MachineId] AS [MachineId],
[Limit1].[WorkItemState_Id] AS [WorkItemState_Id],
[Limit1].[ItemType_Id] AS [ItemType_Id],
[Limit1].[Repaired] AS [Repaired],
[Limit1].[MachineCycle] AS [MachineCycle],
[Limit1].[FirstSeenCheck] AS [FirstSeenCheck],
[Limit1].[LastSeen] AS [LastSeen],
[Limit1].[Archive] AS [Archive],
[Limit1].[CastingDateString] AS [CastingDateString],
[Limit1].[Deleted] AS [Deleted],
[Limit1].[DMC2] AS [DMC2],
[Limit1].[Id1] AS [Id1],
[Limit1].[WorkPlace_Id] AS [WorkPlace_Id],
[Limit1].[CastingFormIdent_Id] AS [CastingFormIdent_Id],
[Limit1].[FormIdentItemType_Id] AS [FormIdentItemType_Id]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[DMC] AS [DMC],
[Extent1].[FirstSeen] AS [FirstSeen],
[Extent1].[DrawingNo] AS [DrawingNo],
[Extent1].[MachineId] AS [MachineId],
[Extent1].[WorkItemState_Id] AS [WorkItemState_Id],
[Extent1].[ItemType_Id] AS [ItemType_Id],
[Extent1].[Repaired] AS [Repaired],
[Extent1].[MachineCycle] AS [MachineCycle],
[Extent1].[FirstSeenCheck] AS [FirstSeenCheck],
[Extent1].[LastSeen] AS [LastSeen],
[Extent1].[Archive] AS [Archive],
[Extent1].[CastingDateString] AS [CastingDateString],
[Extent1].[Deleted] AS [Deleted],
[Extent1].[DMC2] AS [DMC2],
[Extent1].[WorkPlace_Id] AS [WorkPlace_Id],
[Extent1].[CastingFormIdent_Id] AS [CastingFormIdent_Id],
[Extent1].[FormIdentItemType_Id] AS [FormIdentItemType_Id],
[Extent2].[Id] AS [Id1]
FROM WorkItems AS [Extent1]
LEFT OUTER JOIN [dbo].[ChargeItems] AS [Extent2] ON [Extent1].[Id] = [Extent2].[WorkItem_Id]
WHERE ([Extent1].[DMC] = '') OR (([Extent1].[DMC] IS NULL))
) AS [Limit1]
所选的执行计划:
部分SP:
;with chargesToDelete(id, ciid) as (
select c.id, ci.Id from @chargeids c
left join dbo.chargeitems ci on ci.Charge_Id = c.id
where ci.id is null
)
delete from dbo.charges
where Id in (select id from chargesToDelete)
死图XML:
<deadlock>
<victim-list>
<victimProcess id="process6472ad498" />
</victim-list>
<process-list>
<process id="process6472ad498" taskpriority="5" logused="152924" waitresource="PAGE: 5:1:531207 " waittime="794" ownerId="10001638" transactionname="DELETE" lasttranstarted="2018-08-29T11:50:14.510" XDES="0x6ff07f078" lockMode="IX" schedulerid="7" kpid="7620" status="suspended" spid="89" sbid="0" ecid="0" priority="-5" trancount="2" lastbatchstarted="2018-08-29T11:22:53.457" lastbatchcompleted="2018-08-29T11:22:53.457" lastattention="1900-01-01T00:00:00.457" clientapp="Microsoft SQL Server Management Studio - Query" hostname="PCSERVER151" hostpid="6480" loginname="PRC\administrator" isolationlevel="read uncommitted (1)" xactid="10001638" currentdb="5" lockTimeout="4294967295" clientoption1="673187936" clientoption2="390200">
<executionStack>
<frame procname="LP_R.dbo.Archive_Finish" line="190" stmtstart="12876" stmtend="13044" sqlhandle="0x03000500063ecd76e962c80014a9000001000000000000000000000000000000000000000000000000000000">
delete from LP_R.dbo.workitems where id in (select id from @workitemIds); </frame>
<frame procname="LP_R.dbo.Archive" line="64" stmtstart="5142" stmtend="5244" sqlhandle="0x030005007886b57874f2b30014a9000001000000000000000000000000000000000000000000000000000000">
exec Archive_Finish @Day, @Dryrun, @MaxWorkitems; </frame>
<frame procname="adhoc" line="4" stmtstart="62" stmtend="200" sqlhandle="0x0100050010f3f82c309a63770600000000000000000000000000000000000000000000000000000000000000">
EXEC @return_value = [dbo].[Archive]
@Day = 450,
@Dryrun = 0 </frame>
</executionStack>
<inputbuf>
DECLARE @return_value int
EXEC @return_value = [dbo].[Archive]
@Day = 450,
@Dryrun = 0
SELECT 'Return Value' = @return_value
</inputbuf>
</process>
<process id="process66f184558" taskpriority="0" logused="0" waitresource="PAGE: 5:1:114492 " waittime="913" ownerId="10002051" transactionname="SELECT" lasttranstarted="2018-08-29T11:50:15.210" XDES="0x6b379ad00" lockMode="S" schedulerid="5" kpid="3860" status="suspended" spid="67" sbid="2" ecid="0" priority="0" trancount="0" lastbatchstarted="2018-08-29T11:50:15.210" lastbatchcompleted="2018-08-29T11:50:15.210" lastattention="1900-01-01T00:00:00.210" clientapp="EntityFramework" hostname="PCSERVER151" hostpid="3520" loginname="sa" isolationlevel="read committed (2)" xactid="10002051" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="adhoc" line="1" stmtstart="56" sqlhandle="0x02000000412fd7099fe0d3410b538a2193192ac8c5143cf20000000000000000000000000000000000000000">
SELECT
[Limit1].[Id] AS [Id],
[Limit1].[DMC] AS [DMC],
[Limit1].[FirstSeen] AS [FirstSeen],
[Limit1].[DrawingNo] AS [DrawingNo],
[Limit1].[MachineId] AS [MachineId],
[Limit1].[WorkItemState_Id] AS [WorkItemState_Id],
[Limit1].[ItemType_Id] AS [ItemType_Id],
[Limit1].[Repaired] AS [Repaired],
[Limit1].[MachineCycle] AS [MachineCycle],
[Limit1].[FirstSeenCheck] AS [FirstSeenCheck],
[Limit1].[LastSeen] AS [LastSeen],
[Limit1].[Archive] AS [Archive],
[Limit1].[CastingDateString] AS [CastingDateString],
[Limit1].[Deleted] AS [Deleted],
[Limit1].[DMC2] AS [DMC2],
[Limit1].[Id1] AS [Id1],
[Limit1].[WorkPlace_Id] AS [WorkPlace_Id],
[Limit1].[CastingFormIdent_Id] AS [CastingFormIdent_Id],
[Limit1].[FormIdentItemType_Id] AS [FormIdentItemType_Id]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[DMC] AS [DMC],
[Extent1].[FirstSeen] AS [FirstSeen],
[Extent1 </frame>
<frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
(@p__linq__0 nvarchar(4000))SELECT
[Limit1].[Id] AS [Id],
[Limit1].[DMC] AS [DMC],
[Limit1].[FirstSeen] AS [FirstSeen],
[Limit1].[DrawingNo] AS [DrawingNo],
[Limit1].[MachineId] AS [MachineId],
[Limit1].[WorkItemState_Id] AS [WorkItemState_Id],
[Limit1].[ItemType_Id] AS [ItemType_Id],
[Limit1].[Repaired] AS [Repaired],
[Limit1].[MachineCycle] AS [MachineCycle],
[Limit1].[FirstSeenCheck] AS [FirstSeenCheck],
[Limit1].[LastSeen] AS [LastSeen],
[Limit1].[Archive] AS [Archive],
[Limit1].[CastingDateString] AS [CastingDateString],
[Limit1].[Deleted] AS [Deleted],
[Limit1].[DMC2] AS [DMC2],
[Limit1].[Id1] AS [Id1],
[Limit1].[WorkPlace_Id] AS [WorkPlace_Id],
[Limit1].[CastingFormIdent_Id] AS [CastingFormIdent_Id],
[Limit1].[FormIdentItemType_Id] AS [FormIdentItemType_Id]
FROM ( SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[DMC] AS [DMC],
[Extent1].[FirstSeen] AS [F </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="531207" dbid="5" subresource="FULL" objectname="LP_R.dbo.WorkItems" id="lock6d7b2d800" mode="S" associatedObjectId="72057594043891712">
<owner-list>
<owner id="process66f184558" mode="S" />
</owner-list>
<waiter-list>
<waiter id="process6472ad498" mode="IX" requestType="wait" />
</waiter-list>
</pagelock>
<pagelock fileid="1" pageid="114492" dbid="5" subresource="FULL" objectname="LP_R.dbo.WorkItems" id="lock5cd1a2b00" mode="IX" associatedObjectId="72057594043891712">
<owner-list>
<owner id="process6472ad498" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="process66f184558" mode="S" requestType="wait" />
</waiter-list>
</pagelock>
</resource-list>
</deadlock>
答案 0 :(得分:1)
(作为旁注)
delete c
from dbo.charges c
inner join @chargeids t
on t.id = c.id
where not exists(
select 1 from dbo.chargeitems ci
where ci.Charge_id = c.id
)
我想在这个阶段实际上还没有chargeitems
。并且可能在此delete语句之前将它们删除了。因此,归档流程事务可能要比显示的更长。
因此,您的删除过程将删除特定的rows
并在页面上放置一个Intent-X锁,而读取过程则可能以不同的顺序扫描pages
。 FK
s可以放置更多的锁(如果有)。
看看select语句的实际执行计划。它没有顺序地请求TOP 1
,使用远远不够精确的where
谓词,并在chargeitems
上加入workitem_id
(也许上面没有索引)。修复它可能有助于在阅读时摆脱scan
(如果有)。也许您可以尝试选择top1 workitem
,然后再选择top1 chargeitem
。
例如,您可以尝试在读取语句上应用READPAST
提示(不会等待锁定的页面)或将删除语句的粒度提高到PAGLOCK
。如果删除过程很少执行,并且对本系统来说还不错,请尝试TABLOCK
进行删除。
实际上,我错过了要点:您指的是从charges
中删除,而死锁位于WorkItem
上(如死锁图清楚地显示)。但这并不能抵消其余的假设。
如执行计划所示,WorkItem
是真正扫描的,而删除是在特定行上执行的:
delete from LP_R.dbo.workitems where id in (select id from @workitemIds);
您可以应用我帖子中的提示来选择语句和/或删除语句(所有这些都在归档过程中)。
答案 1 :(得分:1)
首先,由于此死锁中有S锁,请考虑将数据库切换为READ COMMITTED SNAPSHOT,以便您的SELECT查询将使用行版本控制而不是S锁来读取数据库。一键解决所有S / X死锁和其他阻塞,但是您需要进行测试。
第二,要解决此僵局,请在存储过程中使用事务,并尽早获得大锁。例如,代替IX锁,它强制使用TABLOCKX提示获取排他表锁。仅当两个会话 first 获得兼容的锁,然后以后尝试获取不兼容的锁时,才会发生死锁。 IX和S锁是此死锁的开始,因此您可以通过确保存储过程不获取弱IX来阻止它,并等待直到它可以获取将使其成功完成的锁为止。 >