Блокировка обновления индекса SQL Server

13

У меня есть 2 запроса, которые при запуске одновременно вызывают тупик.

Запрос 1 - обновить столбец, который включен в индекс (index1):

update table1 set column1 = value1 where id = @Id

Принимает X-Lock на table1, затем пытается X-Lock на index1.

Запрос 2:

select columnx, columny, etc from table1 where {some condition}

Принимает S-Lock для index1, затем пытается S-Lock для table1.

Есть ли способ предотвратить тупик при сохранении тех же запросов? Например, могу ли я как-нибудь применить X-Lock к индексу в транзакции обновления перед обновлением, чтобы обеспечить доступ к таблице и индексу в одном порядке - что должно предотвратить взаимоблокировку?

Уровень изоляции - Read Committed. Блокировки строк и страниц включены для индексов. Вполне возможно, что в обоих запросах участвует одна и та же запись - я не могу судить по графику взаимоблокировок, поскольку он не показывает параметры.

График тупиковой ситуации

Дейл К
источник

Ответы:

11

Есть ли способ предотвратить тупик при сохранении тех же запросов?

График взаимоблокировки показывает, что этот конкретный тупик был взаимоблокировкой преобразования, связанной с поиском по закладке (в данном случае поиск RID):

График тупиковой ситуации

Как отмечается в вопросе, общий риск взаимоблокировки возникает из-за того, что запросы могут получить несовместимые блокировки для одних и тех же ресурсов в разных порядках. SELECTЗапрос необходимо получить доступ к индексу перед таблицей в связи с RID поиска, в то время как UPDATEмодифицирует запрос таблицы, а затем индекс.

Устранение тупика требует удаления одного из компонентов тупика. Ниже приведены основные параметры:

  1. Избегайте поиска RID, создав покрытие некластеризованного индекса. Это, вероятно, не практично в вашем случае, потому что SELECTзапрос возвращает 26 столбцов.
  2. Избегайте поиска RID, создав кластерный индекс. Это может включать создание кластеризованного индекса для столбца Proposal. Это стоит рассмотреть, хотя кажется, что этот столбец имеет тип uniqueidentifier, который может или не может быть хорошим выбором для кластеризованного индекса, в зависимости от более широких проблем.
  3. Избегайте использования общих блокировок при чтении, включив параметры READ_COMMITTED_SNAPSHOTили SNAPSHOTбазы данных. Это потребует тщательного тестирования, особенно в отношении любых встроенных блокировок. Код запуска также потребует тестирования, чтобы убедиться, что логика работает правильно.
  4. Избегайте использования общих блокировок при чтении с использованием READ UNCOMMITTEDуровня изоляции для SELECTзапроса. Все обычные предостережения применяются.
  5. Избегайте одновременного выполнения этих двух запросов с помощью эксклюзивной блокировки приложения (см. Sp_getapplock ).
  6. Используйте подсказки блокировки таблицы, чтобы избежать параллелизма. Это больше, чем вариант 5, поскольку он может повлиять на другие запросы, а не только на два, указанных в вопросе.

Можно ли как-нибудь сделать X-Lock для индекса в транзакции обновления перед обновлением, чтобы обеспечить доступ к таблице и индексу в одном и том же порядке

Вы можете попробовать это, обертывание обновления в явной транзакции и выполнении SELECTс XLOCKнамеком на некластеризованном значении индекса перед обновлением. Это полагается на то, что вы точно знаете текущее значение в некластеризованном индексе, правильно понимаете план выполнения и правильно предугадываете все побочные эффекты этой дополнительной блокировки. Он также полагается на то, что блокирующий двигатель недостаточно умен, чтобы избежать блокировки, если он считается избыточным .

Короче говоря, пока это возможно в принципе, я не рекомендую этого делать. Слишком легко что-то упустить или перехитрить себя творческим путем. Если вы действительно должны избежать этих тупиков (а не просто обнаружить их и повторить попытку), я бы посоветовал вам взглянуть на более общие решения, перечисленные выше.

Пол Уайт 9
источник
Из дальнейшего рассмотрения вопроса я думаю, что оставить его без изменений, вероятно, лучше всего. Это более распространенная проблема, которую я первоначально понял.
Дейл К
1

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

  1. Добавить set deadlock priority low;в избранное. Это приведет к тому, что этот запрос станет жертвой тупика при возникновении тупика.
  2. Настройте логику повторных попыток в вашем приложении, чтобы автоматически повторить выбор, если он потерпел неудачу из-за тупика (или тайм-аута), после ожидания / ожидания в течение короткого периода времени, чтобы позволить блокировать запросы.

Примечание: если вы selectучаствуете в явной транзакции с несколькими операторами, то вам нужно обязательно повторить всю транзакцию, а не только оператор, который не прошел, иначе вы можете получить неожиданные результаты. Если это один , selectто вы хорошо, но если это заявление xв nрамках транзакции, то убедитесь , что вы повторите все nзаявления во время повторной попытки.

BateTech
источник
Спасибо - запросы автоматически становятся тупиковыми жертвами по умолчанию. И да, у нас уже есть надежный механизм повтора на месте.
Дейл К