SQL тупик при удалении, затем массовая вставка
У меня есть проблема с тупиком в SQL Server, которую я не смог решить.
По сути, у меня есть большое количество одновременных подключений (со многих компьютеров), которые выполняют транзакции, где они сначала удаляют диапазон записей, а затем повторно вставляют записи в том же диапазоне с массовой вставкой.
По сути, транзакция выглядит так
BEGIN TRANSACTION T1
DELETE FROM [TableName] WITH( XLOCK HOLDLOCK ) WHERE [Id]=@Id AND [SubId]=@SubId
INSERT BULK [TableName] (
[Id] Int
, [SubId] Int
, [Text] VarChar(max) COLLATE SQL_Latin1_General_CP1_CI_AS
) WITH(CHECK_CONSTRAINTS, FIRE_TRIGGERS)
COMMIT TRANSACTION T1
Массовая вставка вставляет только элементы, соответствующие Id и SubId удаления в одной транзакции. Кроме того, эти записи Id и SubId никогда не должны перекрываться.
Когда у меня достаточно одновременных транзакций этой формы, я начинаю видеть значительное количество тупиковых ситуаций между этими утверждениями.
Я добавил подсказки блокировки XLOCK HOLDLOCK, чтобы попытаться решить проблему, но они, похоже, не помогают.
График канонического тупика для этой ошибки показывает:
Соединение 1:
- Держит RangeX-X на PK_TableName
- Держит IX Page Lock на столе
- Запрос блокировки X-страницы на столе
Соединение 2:
- Держит IX Page Lock на столе
- Запрашивает RangeX-X замок на столе
Что мне нужно сделать, чтобы эти тупики не возникали.
Я немного читал о блокировках RangeX-X и не уверен, что полностью понимаю, что с ними происходит. Есть ли у меня какие-либо варианты, кроме блокировки всей таблицы здесь?
2 ответа
Исходя из ответа Сэма Шафрана:
- Рассмотрите подсказку READPAST, чтобы пропустить все удерживаемые блокировки, если @ID7@SubID является distinc
- Рассмотрим SERIALIZABLE и удалить XLOCK, HOLDLOCK
- Используйте отдельную промежуточную таблицу для массовой вставки, затем скопируйте из нее
Трудно дать вам точный ответ, не имея списка индексов / размера таблицы и т. Д., Однако имейте в виду, что SQL не может захватить несколько блокировок в одном экземпляре. Он будет захватывать блокировки по одной за раз, и если другое соединение уже удерживает блокировку и удерживает блокировку для чего-то, что нужно первой транзакции, kaboom - у вас тупик.
В этом конкретном случае вы можете сделать несколько вещей:
- Убедитесь, что есть индекс (Id, SubId), чтобы SQL мог получить блокировку одного диапазона для удаляемых данных.
- Если взаимные блокировки становятся редкостью, попробуйте снова.
- Вы можете подойти к этому на санках и использовать TABLOCKX, который никогда не заходит в тупик
- Получите точный анализ взаимоблокировок, используя флаг трассировки 1204 http://support.microsoft.com/kb/832524 (чем больше у вас информации о фактическом взаимоблокировке, тем легче его обойти)