Buoni motivi per usare SELECT ... WITH XLOCK?


11

Sto affrontando alcuni deadlock ricorrenti, uno dei quali è un Keylock e contiene una query SELECT con un suggerimento XLOCK che diventa vittima del deadlock. L'altra istruzione è un INSERT in una delle tabelle che fa parte della vista della prima query.

Visualizza:

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

Seleziona query:

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

Dichiarazione INSERT:

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

La tabella sottostante dbo.E contiene circa 3 milioni di righe in circa 20 colonne, alcune delle quali sono ntext.

Eliminando le query e simulandole manualmente con due transazioni, il comportamento è ri-producibile. Il comportamento cambia se XLOCK viene rimosso dalla selezione.

Grafico deadlock:

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

Per quanto ne capisco, sto osservando un deadlock KEYLOCK sostanzialmente causato da una query di indice non coperta che utilizza un indice non cluster e un cluster per raccogliere i valori richiesti, giusto?

Le mie domande:

  1. Non riesco a creare un indice di copertura a causa delle colonne NTEXT richieste. Ridurre drasticamente il numero di righe aiuta qui?
  2. C'è qualche buona ragione per cui non so che SELECT viene eseguito con XLOCK? Lo stallo si verificherebbe anche senza XLOCK?

Risposte:


15

Per quanto ne capisco, sto osservando un deadlock KEYLOCK sostanzialmente causato da una query di indice non coperta che utilizza un indice non cluster e un cluster per raccogliere i valori richiesti, giusto?

In sostanza sì. L'operazione di lettura (seleziona) accede prima all'indice non cluster, quindi all'indice cluster (ricerca). L'operazione di scrittura (inserimento) accede prima all'indice cluster, quindi all'indice non cluster. L'accesso alle stesse risorse in un ordine diverso con blocchi incompatibili può portare a deadlock.

Ridurre drasticamente il numero di righe aiuta qui?

Potrebbe esserlo , poiché vengono bloccate meno risorse e l'operazione tenderà a completarsi più rapidamente. Se aiuta, può ridurre i deadlock, ma molto probabilmente non li elimina (ma continua a leggere).

C'è qualche buona ragione per cui non so che SELECT viene eseguito con XLOCK?

Non proprio. Suggerimenti di blocco come questo sono spesso introdotti da persone senza una piena comprensione di come funzionano l'isolamento, il blocco e i deadlock, nel disperato tentativo di ridurre o eliminare un problema.

Lo stallo si verificherebbe anche senza XLOCK?

No , se la selezione viene effettivamente eseguita in lettura senza commit perché i blocchi incompatibili non verranno presi (e mantenuti) in un ordine diverso.

, se viene utilizzato un livello di isolamento di blocco e i blocchi incompatibili vengono acquisiti e mantenuti in un ordine incoerente, ad esempio condiviso (S) sul non cluster, quindi S sul cluster durante la lettura. La probabilità che si verifichi un deadlock in questo scenario dipende da quanti blocchi vengono eseguiti e per quanto tempo vengono mantenuti.

Consigli

La cosa che spicca davvero (in fase di revisione) è che la transazione selezionata viene eseguita in isolamento serializzabile . Ciò potrebbe essere impostato dal framework o a causa dell'uso del DTC (Distributed Transaction Coordinator). Vedere il nome transazione = "DTCXact" nel grafico del deadlock. Dovresti esaminare le ragioni di ciò e cercare di cambiarlo se possibile.

Senza questa escalation verso serializzabile, è molto probabile che questo deadlock non si verifichi, supponendo che il XLOCKsuggerimento venga rimosso. Detto questo, leggerai sotto isolamento senza impegno , che viene fornito con pochissime garanzie di coerenza.

Se l'applicazione e il codice SQL Server sono in grado di tollerare la lettura di versioni di righe, il passaggio alla lettura dell'isolamento dello snapshot (RCSI) o dell'isolamento dello snapshot (SI) per le letture eviterebbe anche il deadlock ( XLOCKrimosso!), Pur presentando un point-in coerente vista temporale dei dati impegnati. Ciò presuppone anche che è possibile evitare l'isolamento serializzabile, ovviamente.

In definitiva, il XLOCKsuggerimento è controproducente, ma è necessario esaminare il motivo dell'uso del livello di isolamento serializzabile. Il trancount = 2è anche interessante - forse sei transazioni involontariamente nidificazione qui. Qualcos'altro da controllare.


2
  1. Ridurre drasticamente il numero di righe ridurrà la probabilità di ottenere il deadlock, ma non scomparirà completamente.

In termini semplici, la selezione utilizza prima l'indice per determinare le righe da selezionare, quindi recupera le righe, mentre l'inserto inserisce una riga, quindi tenta di aggiornare l'indice (XLOCKED).

  1. Gli sviluppatori di applicazioni tendono a utilizzare XLOCK se nella stessa transazione desiderano successivamente eseguire un aggiornamento dei dati. Ciò garantisce che nessuno possa aggiornare i dati al loro interno. Vorrei indagare su cosa sta facendo l'applicazione per vedere se è richiesto XLOCK.

Detto questo, la rimozione di XLOCK probabilmente non risolverà il problema. SELECT rimuoverà comunque un blocco condiviso sull'indice e INSERT vorrà un XLOCK per aggiornarlo. Sull'oggetto non possono esistere insieme un blocco condiviso e un XLOCK, quindi si otterrà comunque un deadlock. IX_Index1 deve essere MyValue o A o entrambi.

Questo tipo di deadlock si verifica spesso a causa di indici mal progettati e / o troppi indici. O codice scritto male. L'opzione migliore è vedere se esiste un modo per poter riscrivere la selezione per usare un altro indice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.