Тупик оновлення індексу 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 для індексу в транзакції оновлення перед оновленням, щоб переконатися, що таблиця та доступ до індексу в одному порядку - що повинно запобігти тупиковій ситуації?

Рівень ізоляції читається прихильним. Блокування рядків та сторінок увімкнено для покажчиків. Можливо, що в обох запитах бере участь один і той же запис - я не можу сказати з графіка тупикового зв'язку, оскільки він не показує параметри.

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

Відповіді:


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натяком на некластерізованний значенні індексу перед оновленням. Це покладається на те, що ви точно знаєте, що таке поточне значення в некластеризованому індексі, правильний план виконання та правильне передбачення всіх побічних ефектів зняття цього додаткового блокування. Він також покладається на те, що двигун блокування не є достатньо розумним, щоб уникнути зняття блокування, якщо він вважається зайвим .

Коротше кажучи, хоча це в принципі можливо, я не рекомендую його. Занадто легко щось пропустити або перехитрити себе творчо. Якщо ви справді повинні уникати цих тупиків (а не просто їх виявляти та повторювати), я б радив вам заглянути більш загальні рішення, перелічені вище.


Якщо дивитися далі на проблему, я думаю, що залишити її незмінною, мабуть, найкраще. Це більш поширена проблема, яку я спочатку усвідомив.
Дейл К

1

У мене є подібне питання, яке виникає періодично, і ось такий підхід я застосовую.

  1. Додати set deadlock priority low;до вибраного. Це призведе до того, що цей запит стане жертвою тупикової ситуації, коли виникає тупик.
  2. Встановіть логіку повторної роботи у вашій програмі для автоматичного повторного вибору вибору, якщо вона не вдалася через тупик (або час очікування), після очікування / сну протягом короткого періоду часу, щоб дозволити блокування запитів завершуватися.

Примітка. Якщо ваша selectчастина є чіткою транзакцією з декількома заявами, вам потрібно переконатися, що слід повторити всю транзакцію, а не лише те, що не вдалося, інакше ви можете отримати несподівані результати. Якщо це одиночний, selectто у вас все в порядку, але якщо це заява xпро nтрансакцію, тоді просто переконайтеся, що ви повторите всі nзаяви під час повторного спроби.


Спасибі - запити за замовчуванням автоматично стають жертвами тупикового кута. І так, у нас вже є надійний механізм спроби.
Дейл К
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.