Вагомі причини використовувати SELECT… З XLOCK?


11

Я зіткнувся з деякими повторно виникаючими тупиками, один з яких - це Keylock і містить SELECT запит із підказкою XLOCK, який стає жертвою тупикового зв'язку. Інше твердження - ВСТУП в одну з таблиць, яка є частиною подання першого запиту.

Вид:

create view dbo.viewE
 as
    select * from dbo.E  
    where myValue > 13000 

Виберіть запит:

select * from dbo.viewE with (XLOCK) where A > GETUTCDATE() 

Виписка:

INSERT INTO [dbo].[E] (myValue,A) VALUES (10,GetDate())

Основа таблиці dbo.E містить приблизно 3 мільйони рядків приблизно в 20 стовпцях, деякі з них - ntext.

Виконуючи запити та моделюючи їх вручну двома транзакціями, поведінку можна повторно продукувати. Поведінка змінюється, якщо XLOCK вилучено з вибору.

Графік тупикового зв'язку:

<deadlock-list>
 <deadlock victim="process222222221">
  <process-list>
   <process id="process222222221" taskpriority="0" logused="0" waitresource="KEY: 5:72057604035644444 (ccdf51accc0c)" waittime="2522" ownerId="27202256401" transactionname="SELECT" lasttranstarted="2015-09-14T16:32:36.160" XDES="0x2f1ec5ca0" lockMode="RangeX-X" schedulerid="15" kpid="12936" status="suspended" spid="359" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2015-09-14T16:32:36.160" lastbatchcompleted="2015-09-14T16:32:36.160" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="serializable (4)" xactid="27202256401" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="48" sqlhandle="0x02000000611e4523142b2318c47c87313a9b2ba587ff3130">
        SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()      </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@UICulture nvarchar(5))SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()    </inputbuf>
   </process>
   <process id="process6022222" taskpriority="0" logused="161152" waitresource="KEY: 5:72057604035644444 (cd874c2ba438)" waittime="1370" ownerId="27202248438" transactionguid="0x8de5ccd6eeef67469c6234af59e44ca5" transactionname="DTCXact" lasttranstarted="2015-09-14T16:32:34.767" XDES="0x4aa0bf950" lockMode="RangeI-N" schedulerid="14" kpid="6636" status="suspended" spid="329" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-09-14T16:32:37.300" lastbatchcompleted="2015-09-14T16:32:37.300" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="read uncommitted (1)" xactid="27202248438" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="936" sqlhandle="0x020000004853462f09790a4ddedc0d574c2afa539aef1c0e">
     INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock258b6dc80" mode="X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process6022222" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process222222221" mode="RangeX-X" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock7b145c400" mode="RangeX-X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process222222221" mode="RangeX-X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6022222" mode="RangeI-N" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

Наскільки я це розумію, я дивлюся на тупик KEYLOCK, в основному викликаний непокритим запитом індексу, який використовує некластеризований та кластеризований індекс для збору необхідних значень, правда?

Мої запитання:

  1. Я не можу створити індекс покриття через залучені потрібні стовпці NTEXT. Чи допоможе тут різко зменшити кількість рядків?
  2. Чи є якась вагома причина, що я просто не знаю, що SELECT виконується за допомогою XLOCK? Чи б тупик трапився і без XLOCK?

Відповіді:


15

Наскільки я це розумію, я дивлюся на тупик KEYLOCK, в основному викликаний непокритим запитом індексу, який використовує некластеризований та кластеризований індекс для збору необхідних значень, правда?

По суті, так. Операція читання (select) звертається спочатку до некластеризованого індексу, потім до кластерного індексу (пошуку). Операція запису (вставка) звертається спочатку до кластерного індексу, потім до некластеризованого індексу. Доступ до одних і тих же ресурсів у іншому порядку з несумісними блокуваннями може призвести до тупикової ситуації.

Чи допоможе тут різко зменшити кількість рядків?

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

Чи є якась вагома причина, що я просто не знаю, що SELECT виконується за допомогою XLOCK?

Не зовсім. Такі підказки часто вводяться людьми без повного розуміння того, як працюють ізоляція, блокування та тупики, у відчайдушній спробі зменшити чи усунути проблему.

Чи б тупик трапився і без XLOCK?

Ні , якщо вибір насправді працює при відключенні зчитування, оскільки не сумісні блокування не будуть прийняті (і утримуються) в іншому порядку.

Так , якщо використовується рівень ізоляції блокування , а несумісні блокування приймаються та тримаються в непослідовному порядку, наприклад, спільний (S) на некластеризований, то S на кластерному під час читання. Наскільки ймовірний тупик у цьому сценарії, залежить від того, скільки зафіксованих блокувань та як довго вони будуть утримуватися.

Поради

Те, що дійсно виділяється (на перегляді), - це те, що вибрана транзакція працює під серіалізаційною ізоляцією . Це може бути встановлено вашою рамкою, або завдяки використанню DTC (розподіленого координатора транзакцій) - див. Ім'я транзакції = "DTCXact" у графіку тупикового зв'язку. Ви повинні вивчити причини цього і поглянути, щоб змінити це, якщо можливо.

Без цієї ескалації до серіалізаційності великі шанси, що цей глухий кут не відбудеться, якщо припустити, що XLOCKпідказка усунена. Це означає, що ви будете читати під неперечитаною ізоляцією, яка має дуже малі гарантії послідовності.

Якщо ваш додаток та код SQL Server можуть терпіти читання версій рядків, зміна читання ізоляції знімків (RCSI) або ізоляція знімків (SI) для прочитаних також дозволить уникнути глухого кута ( XLOCKвидалено!), Подаючи при цьому послідовну точку введення -час перегляду скоєних даних. Це також передбачає, що, звичайно, ви можете уникнути серіалізаційної ізоляції.

Зрештою, XLOCKпідказка є контрпродуктивною, але вам справді потрібно розібратися в причині використання рівня серіалізаційного ізоляції. trancount = 2Також цікаво - можливо , ви випадково гніздиться угода тут. Щось ще перевірити.


2
  1. Різке зменшення кількості рядків зменшить ймовірність потрапляння в глухий кут, але воно не піде повністю.

Простіше кажучи, для вибору спочатку використовується індекс для визначення рядків для вибору, потім виведення рядків, а вставка вставляє рядок, а потім намагається оновити індекс (XLOCKED).

  1. Розробники додатків, як правило, використовують XLOCK, якщо в одній транзакції вони хочуть пізніше оновити дані. Це гарантує, що ніхто не може оновлювати дані під ними. Я би дослідив, що робить програма, щоб побачити, чи потрібен XLOCK.

Сказавши це, видалення XLOCK, ймовірно, не вирішить проблему. SELECT все одно зніме загальний замок на індексі, а INSERT захоче XLOCK оновити його. Спільний замок і XLOCK не можуть існувати на об'єкті разом, тому ви все одно отримаєте тупик. IX_Index1 має бути або MyValue, або A, або обом.

Цей тип тупикової ситуації часто виникає через погано розроблені індекси та / або занадто багато індексів. Або погано написаний код. Найкращий варіант - побачити, чи є спосіб, який можна переписати, щоб використовувати інший індекс.

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