Почему следующий SQL Server вставляет тупик при запуске в транзакции?
В настоящее время я вставляю запись в таблицу 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 ответа
Рассматриваемый "конвертировать" - это "конвертировать блокировку" из RangeS-S в RangeI-N, никак не связанный с функцией "CONVERT". Тот факт, что у вас есть блокировки RangeS-S, уже помещенные в индекс PK_Tag_1, указывает на то, что вы делаете нечто большее, чем просто INSERT. Делает ли ваша транзакция, случайно, проверку, чтобы увидеть, существует ли новая запись перед попыткой вставки?
Проверьте уровень изоляции, который используется в вашем запросе. Обратите внимание, что TransactionScope по умолчанию использует уровень изоляции Serializable ( http://msdn.microsoft.com/en-us/library/ms172152.aspx). Попробуйте изменить уровень изоляции вашей транзакции на Read Commited.
Вам не нужна транзакция вообще. scope_identity()
Функция вернет идентификатор, созданный последним в той же области видимости, поэтому проблем не возникнет, если до получения идентификатора будет выполнена другая вставка, так как она находится в другой области видимости.