Deadlock di aggiornamento dell'indice di SQL Server


13

Ho 2 query che quando eseguite contemporaneamente causano un deadlock.

Query 1: aggiorna una colonna inclusa in un indice (index1):

update table1 set column1 = value1 where id = @Id

Esegue X-Lock su table1 quindi tenta un X-Lock su index1.

Query 2:

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

Esegue un S-Lock su index1 quindi tenta un S-Lock su table1.

C'è un modo per prevenire il deadlock mantenendo le stesse query? Ad esempio, in qualche modo posso prendere un X-Lock sull'indice nella transazione di aggiornamento prima dell'aggiornamento per garantire che la tabella e l'accesso all'indice siano nello stesso ordine - cosa che dovrebbe impedire il deadlock?

Il livello di isolamento è di tipo commit. I blocchi di riga e pagina sono abilitati per gli indici. È possibile che lo stesso record stia partecipando a entrambe le query - non posso dirlo dal grafico deadlock in quanto non mostra i parametri.

Grafico deadlock

Risposte:


11

C'è un modo per prevenire il deadlock mantenendo le stesse query?

Il grafico del deadlock mostra che questo particolare deadlock era un deadlock di conversione associato a una ricerca di segnalibri (una ricerca RID in questo caso):

Grafico deadlock

Come osserva la domanda, il rischio di deadlock generale sorge perché le query possono ottenere blocchi incompatibili sulle stesse risorse in ordini diversi. La SELECTquery deve accedere all'indice prima della tabella a causa della ricerca RID, mentre la UPDATEquery modifica prima la tabella, quindi l'indice.

L'eliminazione del deadlock richiede la rimozione di uno degli ingredienti del deadlock. Le seguenti sono le opzioni principali:

  1. Evita la ricerca RID creando la copertura dell'indice non cluster. Questo probabilmente non è pratico nel tuo caso perché la SELECTquery restituisce 26 colonne.
  2. Evita la ricerca RID creando un indice cluster. Ciò implicherebbe la creazione di un indice cluster sulla colonna Proposal. Questo vale la pena considerare, sebbene sembri che questa colonna sia di tipo uniqueidentifier, che potrebbe essere o meno una buona scelta per un indice cluster, a seconda di problemi più ampi.
  3. Evitare di applicare blocchi condivisi durante la lettura abilitando le opzioni READ_COMMITTED_SNAPSHOTo del SNAPSHOTdatabase. Ciò richiederebbe test accurati, soprattutto per quanto riguarda eventuali comportamenti di blocco progettati. Il codice di trigger richiederebbe anche dei test per garantire che la logica funzioni correttamente.
  4. Evitare di applicare blocchi condivisi durante la lettura utilizzando il READ UNCOMMITTEDlivello di isolamento per la SELECTquery. Si applicano tutti i soliti avvertimenti.
  5. Evita l'esecuzione simultanea delle due query in questione utilizzando un blocco dell'applicazione esclusivo (vedi sp_getapplock ).
  6. Utilizzare i suggerimenti per il blocco delle tabelle per evitare la concorrenza. Questo è un martello più grande dell'opzione 5, poiché può influire su altre query, non solo sui due identificati nella domanda.

Posso in qualche modo prendere un X-Lock sull'indice nella transazione di aggiornamento prima dell'aggiornamento per garantire che la tabella e l'accesso all'indice siano nello stesso ordine

Puoi provare questo, avvolgendo l'aggiornamento in una transazione esplicita ed eseguendo un SELECTcon un XLOCKsuggerimento sul valore dell'indice non cluster prima dell'aggiornamento. Ciò si basa sul fatto che tu sai con certezza quale sia il valore corrente nell'indice non cluster, ottenendo il piano di esecuzione corretto e anticipando correttamente tutti gli effetti collaterali di questo blocco aggiuntivo. Fa anche affidamento sul fatto che il motore di chiusura non sia abbastanza intelligente da evitare di bloccare la serratura se viene giudicato ridondante .

In breve, sebbene ciò sia fattibile in linea di principio, non lo consiglio. È troppo facile perdere qualcosa o superare se stessi in modo creativo. Se davvero dovessi evitare questi deadlock (piuttosto che solo rilevarli e riprovare), ti incoraggerei a guardare invece alle soluzioni più generali elencate sopra.


Analizzando ulteriormente il problema, penso che lasciarlo invariato sia probabilmente il migliore. È un problema più comune che ho capito inizialmente.
Dale K,

1

Ho un problema simile che si verifica occasionalmente ed ecco l'approccio che seguo.

  1. Aggiungi set deadlock priority low;alla selezione. Ciò farà sì che questa query sia la vittima del deadlock quando si verifica un deadlock.
  2. Configurare la logica dei tentativi all'interno dell'applicazione per riprovare automaticamente la selezione in caso di errore a causa di deadlock (o timeout), dopo aver atteso / sospeso per un breve periodo di tempo per consentire il completamento delle query di blocco.

Nota: se fai selectparte di una transazione multi-statement esplicita, devi essere sicuro di riprovare l'intera transazione e non solo la dichiarazione fallita, altrimenti puoi ottenere alcuni risultati imprevisti. Se questo è un singolo selectallora si sta bene, ma se si tratta di dichiarazioni xdi nall'interno di una transazione, quindi assicuratevi solo di riprovare tutte le ndichiarazioni durante il tentativo.


Grazie - le query sono automaticamente vittime deadlock per impostazione predefinita. E sì, abbiamo già messo in atto un robusto meccanismo di tentativi.
Dale K,
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.