Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

L'anatomia dei deadlock di SQL Server e i modi migliori per evitarli

I professionisti del database devono confrontarsi di routine con problemi di prestazioni del database come indicizzazione impropria e codice scritto male nelle istanze SQL di produzione. Si supponga di aver aggiornato una transazione e SQL Server ha segnalato il seguente messaggio di deadlock. Per i DBA appena agli inizi, questo potrebbe essere uno shock.

In questo articolo esploreremo i deadlock di SQL Server e i modi migliori per evitarli.

Che cos'è un deadlock di SQL Server?

SQL Server è un database altamente transazionale. Si supponga, ad esempio, di supportare il database di un portale di acquisti online in cui si ricevono nuovi ordini dai clienti 24 ore su 24. È probabile che più utenti stiano svolgendo la stessa attività contemporaneamente. In questo caso, il tuo database dovrebbe seguire le proprietà di Atomicità, Consistenza, Isolamento, Durabilità (ACID) per essere coerente, affidabile e proteggere l'integrità dei dati.

L'immagine seguente descrive le proprietà ACID in un database relazionale.

Per seguire le proprietà ACID, SQL Server utilizza meccanismi di blocco, vincoli e registrazione write-ahead. Vari tipi di blocco includono:blocco esclusivo(X), blocco condiviso(S), blocco aggiornamento (U), blocco intento (I), blocco schema (SCH) e blocco aggiornamento in blocco (BU). Questi blocchi possono essere acquisiti a livello di chiave, tabella, riga, pagina e database.

Supponiamo di avere due utenti, John e Peter, collegati al database dei clienti.

  • John vuole aggiornare i record per il cliente che ha [customerid] 1.
  • Allo stesso tempo, Peter vuole recuperare il valore per il cliente che ha [customerid] 1.

In questo caso, SQL Server utilizza i seguenti blocchi sia per John che per Peter.

Serrature per Giovanni

  • Richiede un blocco intento esclusivo (IX) sulla tabella del cliente e sulla pagina che contiene il record.
  • Ci vuole inoltre un blocco (X) esclusivo sulla riga che John vuole aggiornare. Impedisce a qualsiasi altro utente di modificare i dati della riga fino a quando il processo A non rilascia il blocco.

Serrature per Peter

  • Acquisisce un blocco di intent shared (IS) sulla tabella del cliente e sulla pagina che contiene il record come da clausola where.
  • Cerca di prendere un blocco condiviso per leggere la riga. Questa riga ha già un blocco esclusivo per John.

In questo caso, Peter deve aspettare che John finisca il suo lavoro e rilasci il blocco esclusivo. Questa situazione è nota come blocco.

Supponiamo ora che in un altro scenario, John e Peter abbiano i seguenti blocchi.

  • John ha un blocco esclusivo sulla tabella cliente per l'ID cliente 1.
  • Peter ha un blocco esclusivo sulla tabella degli ordini per l'ID cliente 1.
  • John richiede un blocco esclusivo sulla tabella degli ordini per completare la sua transazione. Peter ha già un blocco esclusivo sulla tabella degli ordini.
  • Peter richiede un blocco esclusivo sul tavolo del cliente per completare la sua transazione. John ha già un blocco esclusivo sulla tabella dei clienti.

In questo caso, nessuna delle transazioni può procedere perché ogni transazione richiede una risorsa detenuta dall'altra transazione. Questa situazione è nota come deadlock di SQL Server.

Meccanismi di monitoraggio del deadlock di SQL Server

SQL Server esegue il monitoraggio periodico delle situazioni di deadlock utilizzando il thread di monitoraggio del deadlock. Questo controlla i processi coinvolti in un deadlock e identifica se una sessione è diventata una vittima di deadlock. Utilizza un meccanismo interno per identificare il processo vittima di deadlock. Per impostazione predefinita, la transazione con la minor quantità di risorse necessarie per il rollback è considerata una vittima.

SQL Server interrompe la sessione della vittima in modo che un'altra sessione possa acquisire il blocco richiesto per completare la transazione. Per impostazione predefinita, SQL Server verifica la situazione di deadlock ogni 5 secondi utilizzando il monitoraggio di deadlock. Se rileva un deadlock, potrebbe ridurre la frequenza da 5 secondi a 100 millisecondi a seconda dell'occorrenza del deadlock. Reimposta nuovamente il thread di monitoraggio a 5 secondi se non si verificano frequenti deadlock.

Una volta che SQL Server termina un processo come vittima di deadlock, verrà visualizzato il messaggio seguente. In questa sessione, l'ID processo 69 è stato vittima di un deadlock.

L'impatto dell'utilizzo delle istruzioni di priorità deadlock di SQL Server

Per impostazione predefinita, SQL Server contrassegna la transazione con il rollback meno costoso come vittima di un deadlock. Gli utenti possono impostare la priorità del deadlock in una transazione utilizzando l'istruzione DEADLOCK_PRIORITY.

SET DEADLOCK_PRIORITY

Utilizza i seguenti argomenti:

  • Basso:equivale a priorità deadlock -5
  • Normale:è la priorità di deadlock predefinita 0
  • Alto:è la priorità di deadlock più alta 5.

Possiamo anche impostare valori numerici per la priorità deadlock da -10 a 10 (21 valori totali).

Diamo un'occhiata ad alcuni esempi di istruzioni di priorità deadlock.

Esempio 1:

Sessione 1 con priorità deadlock:Normale (0)> Sessione 2 con priorità deadlock:Bassa (-5)

Vittima di stallo:  Sessione 2

Esempio 2:

Sessione 1 con priorità deadlock:Normale (0)

Vittima di stallo:  Sessione 1

Esempio 3

Sessione 1 con priorità deadlock:-3> Sessione 2 con priorità deadlock:-7

Esempio 4:

Sessione 1 con priorità deadlock:-5

Vittima di stallo:  Sessione 1

Deadlock di SQL Server che utilizzano grafici di deadlock

Un grafico deadlock è una rappresentazione visiva dei processi di deadlock, dei relativi blocchi e della vittima del deadlock. Possiamo abilitare i flag di traccia 1204 e 1222 per acquisire informazioni dettagliate sui deadlock in un formato grafico e XML. Possiamo utilizzare l'evento esteso system_health predefinito per ottenere i dettagli del deadlock. Un modo semplice e veloce per interpretare il deadlock è attraverso un grafico di deadlock. Simuliamo una condizione di deadlock e visualizziamo il relativo grafico di deadlock.

Per questa dimostrazione, abbiamo creato la tabella Clienti e Ordini e inserito alcuni record di esempio.

CREATE TABLE Customer (ID INT IDENTITY(1,1), CustomerName VARCHAR(20)) GO CREATE TABLE Orders (OrderID INT IDENTITY(1,1), ProductName VARCHAR(50)) GO INSERT INTO Customer(CustomerName) VALUES ('Rajendra') Go 100 S INSERT INTO Orders(ProductName) VALUES ('Laptop') Go 100

Quindi, abbiamo aperto una nuova finestra di query e abilitato il flag di traccia a livello globale.

Traceon DBCC(1222,-1)

Dopo aver abilitato il flag di traccia deadlock, abbiamo avviato due sessioni ed eseguito la query nell'ordine seguente:

  • La prima sessione avvia una transazione per aggiornare la tabella cliente per l'ID cliente 1.
  • La seconda sessione avvia una transazione per aggiornare la tabella degli ordini per l'ID ordine 10.
  • La prima sessione tenta di aggiornare la tabella degli ordini per lo stesso ID ordine 10. La seconda sessione blocca già questa riga. La sessione 1 è bloccata a causa dei blocchi della sessione 2.
  • Ora, per la sessione 2, vogliamo aggiornare la tabella dei clienti per l'ID cliente 1. Genera una situazione di deadlock in cui entrambe le sessioni con ID 63 e ID 65 non possono progredire.

In questo esempio, SQL Server sceglie una vittima di deadlock (ID sessione 65) e termina la transazione. Recuperiamo il grafico del deadlock dalla sessione dell'evento esteso system_health.

SELECT XEvent.query('(event/data/value/deadlock)[1]') AS DeadlockGraph FROM ( SELECT XEvent.query('.') AS XEvent FROM ( SELECT CAST(target_data AS XML) AS TargetData FROM sys.dm_xe_session_targets st INNER JOIN sys.dm_xe_sessions s ON s.address = st.event_session_address WHERE s.NAME = ‘system_health’ AND st.target_name = ‘ring_buffer’ ) AS Data CROSS APPLY TargetData.nodes('RingBufferTarget/event[@name="xml_deadlock_report"] ') AS XEventData(XEvent) ) AS source;

Questa query fornisce un XML deadlock che richiede un DBA esperto per interpretare le informazioni.

Salviamo questo XML deadlock utilizzando l'estensione .XDL e quando apriamo il file XDL in SSMS, otteniamo il grafico deadlock mostrato di seguito.

Questo grafico deadlock fornisce le seguenti informazioni:

  • Nodi di processo:  Nell'ovale, ottieni informazioni relative al processo.
  • Nodi di risorse:  I nodi delle risorse (riquadro quadrato) forniscono informazioni sugli oggetti coinvolti nelle transazioni insieme ai blocchi. In questo esempio, mostra i blocchi RID perché non abbiamo indici per entrambe le tabelle.
  • Bordi:  Un bordo collega il nodo del processo e il nodo della risorsa. Mostra il proprietario della risorsa e la modalità di blocco della richiesta.

Rappresenta una vittima di deadlock cancellando l'ovale nel grafico di deadlock.

È possibile acquisire le informazioni sul deadlock di SQL Server nei seguenti modi:

  • Profilatore di SQL Server
  • Eventi estesi di SQL Server
  • Registri degli errori di SQL Server
  • Tracce predefinite in SQL Server

5 tipi di deadlock in SQL Server

1) Deadlock per la ricerca dei preferiti

La ricerca nei segnalibri è un deadlock che si trova comunemente in SQL Server. Si verifica a causa di un conflitto tra l'istruzione select e le istruzioni DML (insert, update and delete). In genere, SQL Server sceglie l'istruzione select come vittima di deadlock perché non causa modifiche ai dati e il rollback è rapido. Per evitare la ricerca del segnalibro, puoi utilizzare un indice di copertura. Puoi anche utilizzare un suggerimento per la query NOLOCK nelle istruzioni select, ma legge i dati non vincolati.

2) Deadlock della scansione dell'intervallo

A volte, utilizziamo un livello di isolamento SERIALIZABLE a livello di server o di sessione. È un livello di isolamento restrittivo per il controllo della concorrenza e può creare blocchi di scansione dell'intervallo anziché blocchi a livello di pagina o riga. Nel livello di isolamento SERIALIZABLE, gli utenti non possono leggere i dati se sono modificati ma in attesa di essere confermati in una transazione. Allo stesso modo, se una transazione legge i dati, un'altra transazione non può modificarli. Fornisce la concorrenza più bassa, quindi dovremmo utilizzare questo livello di isolamento nei requisiti dell'applicazione specifici.

3) Deadlock del vincolo a cascata

SQL Server utilizza la relazione padre-figlio tra le tabelle utilizzando i vincoli di chiave esterna. In questo scenario, se aggiorniamo o eliminiamo un record dalla tabella padre, sono necessari i blocchi della tabella figlio per evitare record orfani. Per eliminare questi deadlock, dovresti sempre modificare prima i dati in una tabella figlio seguito dai dati padre. Puoi anche lavorare direttamente con la tabella padre usando le opzioni DELETE CASCADE o UPDATE CASCADE. Dovresti anche creare indici appropriati nelle colonne della chiave esterna.

4) Deadlock del parallelismo tra le query

Dopo che un utente ha inviato una query al motore di query SQL, Query Optimizer crea un piano di esecuzione ottimizzato. Può eseguire la query in un ordine seriale o parallelo a seconda del costo della query, del grado massimo di parallelismo (MAXDOP) e della soglia di costo per il parallelismo.

In una modalità di parallelismo, SQL Server assegna più thread. A volte per una query di grandi dimensioni in modalità parallelismo, questi thread iniziano a bloccarsi a vicenda. Alla fine, si converte in deadlock. In questo caso, è necessario rivedere il piano di esecuzione, il MAXDOP e la soglia di costo per le configurazioni di parallelismo. Puoi anche specificare il MAXDOP a livello di sessione per risolvere lo scenario di deadlock.

5) Invertire il deadlock dell'ordine degli oggetti

In questo tipo di deadlock, più transazioni accedono agli oggetti in un ordine diverso nel T-SQL. Ciò provoca il blocco tra le risorse per ogni sessione e lo converte in un deadlock. Vuoi sempre accedere agli oggetti in un ordine logico in modo che non porti a una situazione di stallo.

Modi utili per evitare e ridurre al minimo i deadlock di SQL Server

  • Cerca di mantenere le transazioni brevi; questo eviterà di mantenere i lock in una transazione per un lungo periodo di tempo.
  • Accedi agli oggetti in modo logico simile in più transazioni.
  • Crea un indice di copertura per ridurre la possibilità di un deadlock.
  • Crea indici che corrispondano alle colonne della chiave esterna. In questo modo, puoi eliminare i deadlock dovuti all'integrità referenziale a cascata.
  • Imposta le priorità del deadlock utilizzando la variabile di sessione SET DEADLOCK_PRIORITY. Se imposti la priorità di deadlock, SQL Server interrompe la sessione con la priorità di deadlock più bassa.
  • Utilizzare la gestione degli errori utilizzando i blocchi try-catch. Puoi intercettare l'errore di deadlock ed eseguire nuovamente la transazione in caso di una vittima di deadlock.
  • Cambia il livello di isolamento in READ COMMITTED SNAPSHOT ISOLATION o SNAPSHOT ISOLATION. Ciò modifica il meccanismo di blocco di SQL Server. Tuttavia, dovresti prestare attenzione nel modificare il livello di isolamento, poiché potrebbe avere un impatto negativo su altre query.

Considerazioni sul deadlock di SQL Server

I deadlock sono un meccanismo naturale in SQL Server per evitare che la sessione tenga i blocchi e attenda altre risorse. È necessario acquisire le query deadlock e ottimizzarle in modo che non siano in conflitto tra loro. È importante acquisire il blocco per un breve periodo e rilasciarlo, in modo che altre query possano utilizzarlo efficacemente.

I deadlock di SQL Server si verificano e, sebbene SQL Server gestisca internamente le situazioni di deadlock, dovresti provare a ridurle a icona quando possibile. Alcuni dei modi migliori per eliminare i deadlock sono creare un indice, applicare le modifiche al codice dell'applicazione o ispezionare attentamente le risorse in un grafico di deadlock. Per ulteriori suggerimenti su come evitare i deadlock SQL, consulta il nostro post: Evitare i deadlock SQL con l'ottimizzazione delle query.