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

How can I avoid a deadlock between these two SQL statements?

我有两个在独立线程中运行的存储过程,它们在SQL Server 2005上运行。一个过程将新行插入到一组表中,另一个过程从同一组表中删除旧数据。这些过程在表DLevel和Model上陷入僵局。这是模式:

Scrollbar

1
2
3
4
5
while (...)
BEGIN
    DELETE top (1000) FROM DLevel WHERE DFileID = @fileID1
    ...
END

SQL语句2:

1
2
3
4
5
6
7
8
9
10
11
12
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行,因此将在一次通过中完成所有行的删除。

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

编辑1:
重写以更好地阐明问题;新增图片;简化了SQL,并删除了对DLevel表的联接,该联接不会导致死锁。

编辑2:
添加了死锁图的XML。
现在,死锁发生在"模型"表上。与DLevel表类似的Model删除语句和架构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<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>


您可以尝试从Element表中删除DLevel.DFileID的外键。由于您在DLevel.DlevelID上具有主键,并且在Element中将其作为外键引用,因此实际上并不需要DFileID外键。


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


我怀疑删除和插入必须同时发生某种键冲突。.