Почему следующий SQL Server вставляет взаимоблокировку при запуске в транзакции? - PullRequest
6 голосов
/ 15 июня 2009

В настоящее время я вставляю запись в таблицу SQL Server, а затем выбираю идентификатор автоинкремента следующим образом:

(@p0 int,@p1 nvarchar(8))INSERT INTO [dbo].[Tag]([Some_Int], [Tag])
VALUES (@p0, @p1)

SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value] 

(Это было сгенерировано с использованием Linq-to-SQL). По какой-то причине, когда я запускаю этот код внутри транзакции, используя объект TransactionScope с Serializable уровнем изоляции, SQL Server выдает ошибку взаимоблокировки. Я проанализировал события графа взаимоблокировки и обнаружил, что оба вовлеченных процесса ожидают друг от друга выполнения операции преобразования, поскольку я понимаю следующую информацию:

<resource-list>
   <keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176">
    <owner-list>
     <owner id="processc9be40" mode="RangeS-S"/>
    </owner-list>
    <waiter-list>
     <waiter id="processc9ae38" mode="RangeI-N" requestType="convert"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057594101170176" dbid="5" objectname="foo.dbo.Tag" indexname="PK_Tag_1" id="lockb77cdc0" mode="RangeS-S" associatedObjectId="72057594101170176">
    <owner-list>
     <owner id="processc9ae38" mode="RangeS-S"/>
    </owner-list>
    <waiter-list>
     <waiter id="processc9be40" mode="RangeI-N" requestType="convert"/>
    </waiter-list>
   </keylock>
  </resource-list>

Насколько я понимаю, область транзакции не позволит второму процессу выполнить вставку, пока первый не завершит вставку и выбор идентификатора. Однако, похоже, это не так. Может ли кто-нибудь пролить свет на лучший подход к достижению того, что мне нужно, в поточно-ориентированном виде?

- Обновлено -

Просто чтобы отметить; Я на 99% уверен, что между этими двумя процессами нет общего подключения, поскольку каждый создает новый DataContext для связи с базой данных.

- Обновление снова -

Ремус Русану указал, что некоторая пропущенная информация связана с проблемой, я попытался упростить сценарий на основе отчета о графике тупиковой ситуации, но здесь я расширил объяснение. Перед тем, как выполнить вставку, я выполняю запрос на существующую таблицу, чтобы определить, существует ли тег. Если это так, я заканчиваю транзакцию. Если нет, вставка должна продолжаться, и тогда я выполняю обновление, не показанное здесь, для таблицы, в которой первичный ключ имеет Some_Int, хотя обновление предназначено исключительно для последнего измененного значения. Также может быть важно, чтобы таблица тегов имела кластеризованный индекс, состоящий из идентификатора auto inc и Some_Int. Я не думал, что эта последняя часть информации была релевантной, так как я пытался изменить таблицу так, чтобы в качестве первичного ключа / кластеризованного индекса использовалось только поле auto inc.

Спасибо.

Ответы [ 3 ]

7 голосов
/ 15 июня 2009

Рассматриваемый «конвертировать» - это «блокировать конвертирование» из RangeS-S в RangeI-N , никак не связанный с функцией «CONVERT». Тот факт, что у вас есть блокировки RangeS-S, уже размещенные в индексе PK_Tag_1, указывает на то, что вы делаете нечто большее, чем просто INSERT. Делает ли ваша транзакция какой-либо случай, чтобы сначала проверить, существует ли новая запись перед попыткой вставки?

0 голосов
/ 31 января 2010

Проверьте уровень изоляции, который используется в вашем запросе. Обратите внимание, что TransactionScope по умолчанию использует Сериализуемый уровень изоляции (http://msdn.microsoft.com/en-us/library/ms172152.aspx). Попробуйте изменить уровень изоляции транзакции на Read Commited.

0 голосов
/ 15 июня 2009

Вам не нужна транзакция вообще. Функция scope_identity() вернет идентификатор, созданный последним в той же области видимости, поэтому проблем не возникнет, если до получения идентификатора будет выполнена другая вставка, поскольку она находится в другой области действия.

...