Powody, dla których warto wybrać WYBIERZ… Z XLOCK?

11

Mam do czynienia z kilkoma powtarzającymi się zakleszczeniami, z których jeden to Keylock i zawiera zapytanie SELECT z podpowiedź XLOCK, która staje się ofiarą impasu. Drugą instrukcją jest INSERT w jednej z tabel, która jest częścią widoku pierwszego zapytania.

Widok:

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

Wybierz zapytanie:

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

Instrukcja INSERT:

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

Podstawowa tabela dbo.E zawiera około 3 miliony wierszy w około 20 kolumnach, niektóre z nich są ntext.

Wyjmując zapytania i symulując je ręcznie za pomocą dwóch transakcji, zachowanie można odtworzyć. Zachowanie zmienia się, jeśli XLOCK zostanie usunięty z zaznaczenia.

Wykres zakleszczenia:

<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>

O ile rozumiem to, patrzę na zakleszczenie KEYLOCK zasadniczo spowodowane przez niepokryte zapytanie indeksowe, które wykorzystuje indeks nieklastrowany i klastrowany w celu zebrania wymaganych wartości, prawda?

Moje pytania:

  1. Nie mogę utworzyć indeksu obejmującego z powodu wymaganych wymaganych kolumn NTEXT. Czy drastycznie zmniejszy liczbę wierszy?
  2. Czy jest jakiś dobry powód, dla którego po prostu nie wiem, że WYBÓR jest wykonywany za pomocą XLOCK? Czy impas wystąpiłby również bez XLOCK?
Magier
źródło

Odpowiedzi:

15

O ile rozumiem to, patrzę na zakleszczenie KEYLOCK zasadniczo spowodowane przez niepokryte zapytanie indeksowe, które wykorzystuje indeks nieklastrowany i klastrowany w celu zebrania wymaganych wartości, prawda?

Zasadniczo tak. Operacja odczytu (wybierz) najpierw uzyskuje dostęp do indeksu nieklastrowanego, a następnie indeksu klastrowanego (wyszukiwanie). Operacja zapisu (wstawianie) uzyskuje najpierw dostęp do indeksu klastrowanego, a następnie indeks nieklastrowany. Dostęp do tych samych zasobów w innej kolejności z niekompatybilnymi zamkami może doprowadzić do impasu.

Czy drastycznie zmniejszy liczbę wierszy?

Może się tak stać , ponieważ mniej zasobów jest zablokowanych, a operacja będzie się kończyć szybciej. Jeśli to pomoże, może zmniejszyć zakleszczenia, ale najprawdopodobniej ich nie wyeliminuje (ale czytaj dalej).

Czy jest jakiś dobry powód, dla którego po prostu nie wiem, że WYBÓR jest wykonywany za pomocą XLOCK?

Nie całkiem. Takie wskazówki dotyczące blokowania są często wprowadzane przez ludzi bez pełnego zrozumienia, jak działają izolacja, blokowanie i zakleszczenia, w desperackiej próbie ograniczenia lub wyeliminowania problemu.

Czy impas wystąpiłby również bez XLOCK?

Nie , jeśli zaznaczenie faktycznie działa z odczytem niezaangażowanej izolacji, ponieważ niekompatybilne blokady nie będą brane (i trzymane) w innej kolejności.

Tak , jeśli używany jest poziom izolacji blokowania , a niekompatybilne blokady są podejmowane i utrzymywane w niespójnej kolejności, na przykład współdzielone (S) w nieklastrowanym, a następnie S w klastrowanym podczas odczytu. To, jak prawdopodobne jest zakleszczenie w tym scenariuszu, zależy od liczby pobranych blokad i czasu ich utrzymywania.

Rada

Tym, co naprawdę się wyróżnia (podczas przeglądu), jest to, że wybrana transakcja działa w ramach możliwej do serializacji izolacji . Może to być ustawione przez twój framework lub z powodu użycia DTC (Distributed Transaction Coordinator) - patrz nazwa transakcji = „DTCXact” na wykresie impasu. Powinieneś przyjrzeć się przyczynom i zmienić to, jeśli to możliwe.

Bez eskalacji do serializacji istnieje duża szansa, że ​​impas nie wystąpi, zakładając, że XLOCKpodpowiedź zostanie usunięta. To powiedziawszy, będziesz czytać pod przeczytaną nieprzydzieloną izolacją, która ma bardzo niewiele gwarancji spójności.

Jeśli twoja aplikacja i kod SQL Servera mogą tolerować czytanie wersji wierszy, zmiana na odczyt zatwierdzonej izolacji migawek (RCSI) lub izolacji migawek (SI) dla odczytów również pozwoliłaby uniknąć impasu ( XLOCKusuniętego!), Prezentując jednocześnie spójne, bezpośrednie wejście - podgląd zatwierdzonych danych w czasie. Zakłada się również, że można oczywiście uniknąć izolacji możliwej do serializacji.

Ostatecznie XLOCKwskazówka jest nieproduktywne, ale naprawdę trzeba spojrzeć na przyczyny korzystania z serializable poziom izolacji. Jest trancount = 2to również interesujące - być może przypadkowo zagnieżdżasz tutaj transakcje. Coś jeszcze do sprawdzenia.

Paul White 9
źródło
2
  1. Drastyczne zmniejszenie liczby wierszy zmniejszy prawdopodobieństwo wystąpienia impasu, ale nie zniknie całkowicie.

Mówiąc prościej, select najpierw używa indeksu do określenia wierszy do wybrania, a następnie pobiera wiersze, podczas gdy wstawka wstawia wiersz, a następnie próbuje zaktualizować indeks (XLOCKED).

  1. Twórcy aplikacji zwykle używają XLOCK, jeśli w tej samej transakcji chcą później dokonać aktualizacji danych. Zapewnia to, że nikt nie może zaktualizować danych pod nimi. Sprawdziłbym, co robi aplikacja, aby sprawdzić, czy wymagany jest XLOCK.

Powiedziawszy to, usunięcie XLOCK prawdopodobnie nie rozwiąże problemu. SELECT nadal wyjmie blokadę współdzieloną indeksu, a INSERT będzie chciał, aby XLOCK go zaktualizował. Wspólny zamek i XLOCK nie mogą istnieć razem na obiekcie, więc nadal będziesz mieć impas. IX_Index1 musi mieć wartość MyValue, A lub oba.

Ten typ zakleszczenia często występuje z powodu źle zaprojektowanych indeksów i / lub zbyt wielu indeksów. Lub źle napisany kod. Najlepszą opcją jest sprawdzenie, czy istnieje jakiś sposób, aby można było ponownie napisać zaznaczenie i użyć innego indeksu.

Leo Miller
źródło