2010-06-24 131 views
5

我有两个存储过程在单独的线程中运行,运行在SQL Server 2005上。一个过程将新行插入一组表中,一个过程从同一组表中删除旧数据。这些程序在表DLevel模型上运行时出现死锁。下面是模式:如何避免这两个SQL语句之间的死锁?

Scrollbar Image http://www.barramsoft.com/pub/images/DeadLock2.jpg

DFile:主键= DFileID
DLEVEL:主键= DLevelID,外键:DFileID
型号:主键= ModelID,外键:DFileID
ELement:主键= ElementID,外键1:DFileID,外键Key2: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表平均约。 60行,单个DFileID,因此将一次完成删除所有行。

如何避免这两个SQL语句之间的死锁?

编辑1: 重写以更好地阐明问题;添加图片;简化了SQL并删除了连接到DLevel表,这并没有导致死锁。

编辑2: 添加了死锁图形的XML。
死锁现在发生在型号表中。 型号类似的删除语句和架构至于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> 
+0

哪个版本的SQL服务器?我敢打赌,这是在SQL 2000中! – 2010-06-24 18:33:11

+0

DLevel和Element之间是否有任何外键约束? – 2010-06-24 18:34:17

+0

Broken Link:版本是SQL Server 2005 Express。 – Elan 2010-06-24 18:38:29

回答

0

我怀疑在删除和插入同时进行时必定会有某种键违例。

1

一种可能的情况:一个连接在Element中插入一行,并且该行指向DLevel中的一行,并且DLevel中的该行将被另一个连接删除。您的nolock提示不适用于外键。

+0

这些语句在不同的连接中执行。由插入和删除语句引用的行(DLevel表)总是不同的,因为DFileID在两者之间不同。如果nolock不适用于外键,那么这两个语句可能会碰到页面锁定冲突。我知道关闭行和页面锁定,但有什么办法强制行级锁定? – Elan 2010-06-24 19:42:52

+0

在“insert into Element ...”查询中删除了与DLevel表的联接。仍然陷入僵局。 – Elan 2010-06-25 15:10:28

1

您可以尝试从元素表中删除外键到DLevel.DFileID。由于在DLevel.DlevelID上有一个主键,并且您将它作为元素中的外键引用,所以不需要DFileID外键。