Как мне избежать тупика между этими двумя операторами SQL?

У меня есть две хранимые процедуры, работающие в отдельных потоках, работающих на SQL Server 2005. Одна процедура вставляет новые строки в набор таблиц, а другая процедура удаляет старые данные из того же набора таблиц. Эти процедуры зашли в тупик для таблиц DLevel и Model. Вот схема:

Изображение полосы прокрутки
(источник: barramsoft .com)

Таблица DFile: первичный ключ = DFileID
Таблица DLevel: первичный ключ = DLevelID, внешний ключ: DFileID
Таблица Модель: первичная Ключ = ModelID, Внешний ключ: DFileID
Таблица ELement: Первичный ключ = 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 в среднем ок. 60 строк для одного DFileID и, таким образом, завершит удаление всех строк за один проход.

Как мне избежать тупика между этими двумя операторами SQL?

Изменить 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>

person Elan    schedule 24.06.2010    source источник
comment
какая версия SQL-сервера? Бьюсь об заклад, это в SQL 2000 !!   -  person Broken Link    schedule 24.06.2010
comment
Существуют ли какие-либо ограничения внешнего ключа между DLevel и Element?   -  person A-K    schedule 24.06.2010
comment
Неработающая ссылка: версия - SQL Server 2005 Express.   -  person Elan    schedule 24.06.2010
comment
AlexKuznetsov: Да, на DFileID и LevelNum.   -  person Elan    schedule 24.06.2010
comment
Если вы добавите график тупиков, вы избавитесь от множества догадок из уравнения. msdn.microsoft.com/en-us/library/ms190465.aspx   -  person Remus Rusanu    schedule 24.06.2010
comment
@Remus: Спасибо за предложение. Я запустил график тупиков. Я получаю блокировку страницы на Element и блокировку клавиш на DLevel. Мне нужно изучить это дальше, чтобы полностью понять это. Я упростил SQL и все еще захожу в тупик.   -  person Elan    schedule 25.06.2010
comment
@Elan - Можете ли вы вставить XML-версию диаграммы взаимоблокировок в свой вопрос?   -  person Martin Smith    schedule 25.06.2010
comment
@Martin: Добавлена ​​XML-версия графика тупиков.   -  person Elan    schedule 25.06.2010
comment
@Elan - На чем блокируется страница? SELECT OBJECT_NAME(i.object_id), i.name, p.partition_id FROM sys.partitions AS p INNER JOIN sys.indexes AS i ON i.object_id = p.object_id AND i.index_id = p.index_id WHERE p.partition_id = 72057594039107584   -  person Martin Smith    schedule 25.06.2010
comment
@Martin - ИМЯ ОБЪЕКТА: элемент, i.name = PK_Element, p.partition_id = 72057594039107584   -  person Elan    schedule 25.06.2010
comment
Я думаю, что происходит 1. Удаление из модели запрашивает блокировку общей страницы на странице с идентификатором 1946 (PK_Element в CadExplorer5.dbo.Element), которая удерживается оператором вставки. Он делает это, поскольку он должен прочитать Element, чтобы убедиться, что удаление не нарушит никаких ограничений внешнего ключа и не оставит потерянные записи в Element. 2 Оператор Insert в Element запрашивает блокировку общего ключа на PK_Model (в CadExplorer5.dbo.Model), которая удерживается оператором delete. Он делает это, поскольку он должен проверить, что вставка не нарушает никаких ограничений внешнего ключа. Если так, я не знаю, как этого избежать.   -  person Martin Smith    schedule 25.06.2010
comment
@Martin - Большое спасибо за помощь мне в этом. Есть ли у вас какие-нибудь предложения, как я могу выйти из тупика? Я попробовал исключительную блокировку таблицы в таблице модели перед выполнением удаления (таким образом, принудительно заблокировав вставку в таблице элементов). Это значительно уменьшило количество тупиковых ситуаций, но я все еще получаю тупиковые ситуации!?!   -  person Elan    schedule 25.06.2010
comment
@Elan - Нет, я все еще пытаюсь узнать больше об этой области сам, поэтому отнеситесь к моему анализу с долей скепсиса. Надеюсь, кто-то еще подтвердит, есть ли в нем какие-либо достоинства! В этой статье обсуждается, возможно, аналогичная проблема, хотя sqlblogcasts.com/blogs/tonyrogerson/archive/2007/03/30/.   -  person Martin Smith    schedule 25.06.2010
comment
Если вы денормализуете и удалите все иностранные ключи, этот тупик исчезнет (похоже, он связан с ограничениями fk)   -  person Sam Saffron    schedule 01.07.2010
comment
@Sam: На данный момент, похоже, все сводится к ограничениям внешнего ключа, и я верю, что тупик исчезнет, ​​если я их удалю. Мне нужно проверить и подтвердить. Также кажется, что причиной этого является блокировка страницы при вставке в таблицу элементов. Очистка существующих данных с внешними ключами - не редкость! Должно быть решение этой проблемы без снятия ограничений внешнего ключа.   -  person Elan    schedule 01.07.2010


Ответы (3)


Один из возможных сценариев: одно соединение вставляет строку в Element, и эта строка ссылается на строку в DLevel, и эта строка в DLevel удаляется другим подключением. Ваша подсказка nolock не применяется к внешним ключам.

person A-K    schedule 24.06.2010
comment
Эти операторы выполняются в отдельных соединениях. Строки (таблицы DLevel), на которые ссылаются операторы вставки и удаления, всегда различаются, так как их DFileID различается. Если nolock не применяется к внешним ключам, возможно, эти два оператора приводят к конфликтам блокировки страниц. Мне известно об отключении блокировки строк и страниц, но есть ли способ принудительно заблокировать на уровне строк? - person Elan; 24.06.2010
comment
Удалено присоединение к таблице DLevel во вставке в запрос Element ... По-прежнему заходим в тупик. - person Elan; 25.06.2010

Вы можете попробовать удалить внешний ключ для DLevel.DFileID из таблицы элементов. Поскольку у вас есть первичный ключ на DLevel.DlevelID и вы ссылаетесь на него как на внешний ключ в Element, внешний ключ DFileID на самом деле не нужен.

person MikeTWebb    schedule 25.06.2010

Я подозреваю, что должно быть какое-то нарушение ключа с одновременным удалением и вставкой ..?

person Broken Link    schedule 24.06.2010