Таблок подсказка вызывает тупики

10

Я вставлял два набора данных, используя минимальное ведение журнала, в пустую таблицу кучи с помощью двух параллельно выполняемых задач SQL и с помощью SQL следующей формы.

INSERT INTO Table (TABLOCK) SELECT FROM ...

После того, как задание немного зависло, одна из задач SQL стала жертвой тупика. Ниже приведен XML-вывод графа взаимоблокировки.

Может кто-нибудь объяснить, что происходило под капотом?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

Все становится намного сложнее, потому что я обнаружил, что в большинстве случаев две задачи «Выполнение SQL» могут успешно выполняться параллельно. Попробуйте ниже:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

Поскольку единственным отличием является оператор SELECT ... FROM ..., похоже, что оператор SELECT ... FROM ... может повлиять на режим блокировки здесь?

SqlWhale
источник
Вы можете указать TABLOCKX вместо TABLOCK, чтобы предотвратить взаимоблокировку. Хотя это также сериализовало бы доступ к таблице, вы все равно получали бы минимальное ведение журнала.
Дан Гусман

Ответы:

8

Руководство по производительности загрузки данных было написано для SQL Server 2008, но, насколько я могу судить, Microsoft не внесла никаких улучшений в этой области для куч. Вот цитата для вашего сценария загрузки:

Массовая загрузка пустой таблицы без разделов

Загрузка данных в неразделенную таблицу, будучи простой операцией, может быть оптимизирована несколькими способами.

...

Множественные одновременные операции вставки для кучи возможны только в том случае, если выбранный массовый метод вызывает блокировку массового обновления (BU) для таблицы. Две блокировки массового обновления (BU) совместимы, и, следовательно, две операции массового обновления могут выполняться одновременно.

В этом сценарии и INSERT… SELECT, и SELECT INTO имеют недостаток. Обе эти операции принимают исключительную (X) блокировку на уровне таблицы в месте назначения. Это означает, что в данный момент времени может выполняться только одна операция массовой загрузки, что ограничивает масштабируемость. Однако BCP, BULK INSERT и Integration Services способны принимать блокировки массового обновления (BU) - если указать подсказку TABLOCK.

Важной частью является то, что вы не получите блокировку BU с INSERT ... SELECT. Вы всегда получите эксклюзивную блокировку на столе, так что только одна INSERTможет работать одновременно.

В комментариях вы сказали, что вы вставите 100 000 строк или меньше, и что другие процессы не будут выполняться в таблицах во время вставок. При отправке двух запросов INSERT в базу данных я ожидаю, что произойдет одно из трех:

  1. Одна вставка запускается первой и блокирует другую вставку. Вторая вставка ждет, пока первая вставка не будет завершена.
  2. Одна вставка заканчивается до начала второй вставки. Нет явной блокировки, но они не запускаются одновременно.
  3. Вы получаете тупик, и только одна вставка завершается успешно.

Во всех случаях вы либо получаете пользу, либо не получаете вреда, добавляя TABLOCKXподсказку к запросу, поэтому я рекомендую обходить тупик. Если вы хотите знать, почему иногда возникает тупик, вам нужно искать другой ответ для этого.

В другом сценарии, в котором вам действительно требуется параллельная вставка, два способа решения проблемы BU состоят в том, чтобы разделить кучу и каждый сеанс вставить в отдельный раздел или загрузить данные через BCP, BULK INSERT или Integration Services. ,

Джо Оббиш
источник
Спасибо за ответ, но ситуация, с которой я столкнулся, зашла в тупик - это единственный случай, который у меня есть. В большинстве случаев при параллельном выполнении команды INSERT INTO WITH (TABLOCK) SELECT FROM задание не будет выполнено. Кстати, я использую SQL SERVER 2008 R2. Я добавил успешный пример в свой вопрос.
SqlWhale
@tec в тех случаях, когда это не выходит из строя, предположительно, они фактически работают последовательно. Возможно, вы столкнулись с проблемой только тогда, когда есть определенное время запуска, например, для составления плана.
Мартин Смит
@MartinSmith Запрос, в котором я столкнулся с проблемой в проекте, гораздо сложнее, чем пример SELECT 1, который требует больше затрат на компиляцию, но он просто читает из нескольких таблиц. Я пытаюсь воспроизвести тупик этого типа с помощью более сложных запросов.
SqlWhale
@TecKnowNothing О том, сколько строк вы вставляете? Сколько раз в день запускается процесс? ВЫБИРАЮТ ли другие запросы из таблицы при загрузке данных?
Джо Оббиш
@JoeObbish 1. Я не думаю, что количество строк здесь является проблемой, у нас есть только 4000 - 70000 для каждого запроса, но они представляют собой сильно агрегированные данные для использования куба. 2. Он все еще находится на стадии тестирования и должен запускаться один раз в день. 3. Больше ничего не читает из целевой таблицы.
SqlWhale
4

Вы вставляете данные dbo.TargetTableиз двух сессий, и оба используют TABLOCKhint.Both, process9609dc8и process5e13048удержание процесса Sch-Sи IXблокировки, которые совместимы друг с другом, поэтому оба процесса могут удерживаться одновременно. Но оба хотят преобразовать IXблокировку в Exclusive Xтип. Xзамки не совместимы друг с другом. Таким образом, SQL-сервер выбрал один из сеансов в качестве жертвы тупика, вместо того чтобы бесконечно ждать друг друга.

Основная тупиковая информация.

Диаграмма совместимости блокировки (Database Engine).

Обнаружение и устранение тупиков.

SqlWorldWide
источник