如何避免这两个 SQL 语句之间出现死锁?
Posted
技术标签:
【中文标题】如何避免这两个 SQL 语句之间出现死锁?【英文标题】:How can I avoid a deadlock between these two SQL statements? 【发布时间】:2010-06-24 18:09:44 【问题描述】:我有两个存储过程在不同的线程中运行,在 SQL Server 2005 上运行。一个过程将新行插入一组表中,另一个过程从同一组表中删除旧数据。这些过程在 DLevel 和 Model 表上遇到了死锁。这是架构:
(来源:barramsoft.com)
表 DFile:主键 = DFileID 表 DLevel:主键 = DLevelID,外键:DFileID 表 Model:主键 = ModelID,外键:DFileID 表元素:主键=ElementID,外键1:DFileID,外键2:DLevelID
我已经隔离了导致死锁的确切的两条 SQL 语句(每个存储过程中的一条)。我已经看到任何一个程序报告的死锁。在这两种情况下我都使用 top (1000) 并且两个语句都在循环中执行,直到它们完成并且没有剩余行可以删除/插入。
SQL 语句 1:
while (...)
begin
delete top (1000) from DLevel where DFileID = @fileID1
...
end
SQL 语句 2:
while (...)
begin
insert into Element (ElementID, DFileID, LevelNum, ...)
select top (1000) el.ElementID, el.DFileID, el.LevelNum, ...
from ElementLoader el with (nolock)
left outer join Element e with (nolock)
on e.ElementID = el.ElementID
where el.DFileID = @fileID2
and e.ElementID is null
order by el.ElementID
...
end
注意:@fileID1 和 @fileID2 的值始终保证不同。 DLevel 表平均有大约。单个 DFileID 有 60 行,因此将在一次传递中完成所有行的删除。
编辑 1: 重写以更好地澄清问题;添加图像;简化 SQL 并删除了对 DLevel 表的连接,这不会导致死锁。
编辑 2: 添加了死锁图的 XML。Model 表现在发生死锁。 Model 的删除语句和架构与 DLevel 表类似。
<deadlock victim="process2bae38">
<process-list>
<process id="process2bae38" taskpriority="0" logused="4760" waitresource="PAGE: 11:1:1946" waittime="46" ownerId="4127445" transactionname="DELETE" lasttranstarted="2010-06-24T16:19:00.107" XDES="0xffffffff90552ae0" lockMode="S" schedulerid="1" kpid="14252" status="suspended" spid="58" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-06-24T16:19:00.107" lastbatchcompleted="2010-06-24T16:19:00.107" clientapp=".Net SqlClient Data Provider" hostname="LT0103" hostpid="1668" loginname="NT AUTHORITY\SYSTEM" isolationlevel="read committed (2)" xactid="4127445" currentdb="11" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="CadExplorer5.dbo.pCleanUpOldFiles" line="364" stmtstart="23718" stmtend="23834" sqlhandle="0x03000b00fb1c2229b1a7f7009f9d00000100000000000000">
delete top (@batchSize) from Model where DFileID = @fileID </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 11 Object Id = 690101499] </inputbuf>
</process>
<process id="process2c95b8" taskpriority="0" logused="283388" waitresource="KEY: 11:72057594039304192 (8100bdf15e8b)" waittime="78" ownerId="4127412" transactionname="INSERT" lasttranstarted="2010-06-24T16:19:00.103" XDES="0xffffffff81d5ef18" lockMode="S" schedulerid="2" kpid="8460" status="suspended" spid="63" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-06-24T16:18:59.413" lastbatchcompleted="2010-06-24T16:18:59.413" clientapp=".Net SqlClient Data Provider" hostname="LT0103" hostpid="1668" loginname="NT AUTHORITY\SYSTEM" isolationlevel="read committed (2)" xactid="4127412" currentdb="11" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
<executionStack>
<frame procname="CadExplorer5.dbo.pLoadElements" line="288" stmtstart="28796" stmtend="33194" sqlhandle="0x03000b00a689fe2b2c5107019f9d00000100000000000000">
insert into Element (
ElementID, DFileID, ModelID, ElementTypeID, CADElementID,
ParentID,
LevelNum,
Depth, NumChildren,
Color, Weight, Style, Xlo, Ylo, Zlo, Xhi, Yhi, Zhi,
Diagonal, XCenter,
BitFlags, ElementModTime
)
select top (@batchSize)
el.ElementID, el.DFileID, el.ModelID, el.ElementTypeID, el.CADElementID,
parent.ElementID as ParentID,
(case when el.LoaderType = 1 and et.IsGraphical = 1 then el.LevelAttrib else null end) as LevelNum,
--l.LevelNum,
el.Depth, el.NumChildren,
el.Color, el.Weight, el.Style, el.Xlo, el.Ylo, el.Zlo, el.Xhi, el.Yhi, el.Zhi,
el.Diagonal, el.XCenter, </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 11 Object Id = 738101670] </inputbuf>
</process>
</process-list>
<resource-list>
<pagelock fileid="1" pageid="1946" dbid="11" objectname="CadExplorer5.dbo.Element" id="lockffffffff86ffd080" mode="IX" associatedObjectId="72057594039107584">
<owner-list>
<owner id="process2c95b8" mode="IX"/>
</owner-list>
<waiter-list>
<waiter id="process2bae38" mode="S" requestType="wait"/>
</waiter-list>
</pagelock>
<keylock hobtid="72057594039304192" dbid="11" objectname="CadExplorer5.dbo.Model" indexname="PK_Model" id="lockffffffff8d66db80" mode="X" associatedObjectId="72057594039304192">
<owner-list>
<owner id="process2bae38" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process2c95b8" mode="S" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
【问题讨论】:
哪个版本的 SQL 服务器?我敢打赌这是在 SQL 2000 中!! 如果添加死锁图,您将需要大量猜测。 msdn.microsoft.com/en-us/library/ms190465.aspx @Elan - 您可以将 XML 版本的死锁图粘贴到您的问题中吗? 我认为正在发生的事情是 1 从模型中删除请求页面 ID 1946 上的共享页面锁定(CadExplorer5.dbo.Element 中的 PK_Element)由插入语句保存。它这样做是因为它必须读取 Element 以检查删除不会违反任何外键约束并在 Element 中留下孤立的记录。 2 向 Element 插入语句请求删除语句持有的 PK_Model(在 CadExplorer5.dbo.Model 中)上的共享密钥锁。它这样做是因为它必须检查插入不会违反任何外键约束。如果是这样,我不确定如何防止这种情况。 @Elan - 不,我不知道我自己还在尝试更多地了解这个领域,所以我的分析要多加一点盐。希望其他人会确认它是否有任何优点!本文通过sqlblogcasts.com/blogs/tonyrogerson/archive/2007/03/30/… 讨论了一个可能类似的问题。 【参考方案1】:一种可能的情况:一个连接在 Element 中插入一行,该行引用 DLevel 中的一行,而 DLevel 中的该行正在被另一个连接删除。您的 nolock 提示不适用于外键。
【讨论】:
这些语句在单独的连接中执行。插入和删除语句引用的(DLevel 表的)行总是不同的,因为两者之间的 DFileID 不同。如果 nolock 不适用于外键,那么这两个语句可能会遇到页面锁定冲突。我知道关闭行和页面锁定,但是有什么方法可以强制行级锁定? 删除了“插入元素...”查询中对 DLevel 表的连接。仍然陷入僵局。【参考方案2】:您可以尝试从元素表中删除 DLevel.DFileID 的外键。由于您在 DLevel.DlevelID 上有一个主键,并且您在 Element 中将其作为外键引用,因此实际上并不需要 DFileID 外键。
【讨论】:
【参考方案3】:我怀疑删除和插入同时进行时一定有某种键违规..?
【讨论】:
以上是关于如何避免这两个 SQL 语句之间出现死锁?的主要内容,如果未能解决你的问题,请参考以下文章