Domanda

Sto riscontrando deadlock SQL molto rari ma fastidiosi su una webapp .NET 2.0 in esecuzione su MS SQL Server 2005. In passato, abbiamo gestito i deadlock SQL in modo molto empirico, sostanzialmente modificando le query fino a funziona.

Tuttavia, ho trovato questo approccio molto insoddisfacente: richiede tempo e inaffidabile. Preferirei molto seguire modelli di query deterministici che garantissero di progettazione che non si verificherà mai un deadlock SQL.

Ad esempio, nella programmazione multithread C #, una semplice regola di progettazione come i blocchi devono essere adottati seguendo il loro ordine lessicografico garantisce che non si verifichino deadlock mai .

Esistono schemi di codifica SQL garantiti a prova di deadlock?

È stato utile?

Soluzione

Scrivere codice a prova di deadlock è davvero difficile. Anche quando accedi alle tabelle nello stesso ordine potresti comunque ottenere deadlock [1]. Ho scritto un post sul mio blog che elabora attraverso alcuni approcci che ti aiuteranno a evitare e risolvere situazioni di deadlock.

Se vuoi assicurarti che due estratti conto / transazioni non si blocchino mai, potresti essere in grado di raggiungerlo osservando quali blocchi consuma ogni estratto conto usando la procedura memorizzata nel sistema sp_lock . Per fare questo devi essere molto veloce o utilizzare una transazione aperta con un suggerimento di blocco.


Note:

  1. Qualsiasi istruzione SELECT che necessita di più di un blocco alla volta può eseguire un deadlock a fronte di una transazione progettata in modo intelligente che afferra i blocchi in ordine inverso.

Altri suggerimenti

Zero deadlock è fondamentalmente un problema incredibilmente costoso nel caso generale perché è necessario conoscere tutte le tabelle / oggetti che si intende leggere e modificare per ogni transazione in esecuzione (questo include SELECTs). La filosofia generale si chiama ordinamento rigoroso blocco a due fasi (da non confondere con il commit a due fasi) ( http://en.wikipedia.org/wiki/Two_phase_locking ; anche 2PL non garantisce nessun deadlock)

Pochissimi DBMS implementano in realtà 2PL rigorosi a causa dell'enorme impatto sulle prestazioni che una cosa del genere provoca (non ci sono pranzi gratuiti) mentre tutte le transazioni attendono che vengano eseguite anche semplici istruzioni SELECT.

Comunque, se questo è qualcosa che ti interessa davvero, dai un'occhiata a SET ISOLATION LEVEL in SQL Server. Puoi modificarlo se necessario. http://en.wikipedia.org/wiki/Isolation_level

Per ulteriori informazioni, consultare Wikipedia sulla serializzazione: http://en.wikipedia.org/wiki/Serializability

Detto questo, una grande analogia è come le revisioni del codice sorgente: controlla presto e spesso. Mantieni le tue transazioni piccole (nel numero di istruzioni SQL, nel numero di righe modificate) e veloci (il tempo di clock aiuta a evitare collisioni con altri). Può essere bello e ordinato fare MOLTE cose in una singola transazione - e in generale sono d'accordo con quella filosofia - ma se si verificano molti deadlock, è possibile suddividere il trasferimento in più piccoli e quindi controlla il loro stato nell'applicazione mentre avanzi. TRAN 1 - OK S / N? Se Y, invia TRAN 2 - OK S / N? ecc. ecc.

A parte questo, nei miei molti anni in cui sono stato un DBA e anche uno sviluppatore (di app DB multiutente che misurano migliaia di utenti simultanei) non ho mai trovato deadlock come un problema così grande che avevo bisogno di una sua conoscenza speciale (o per cambiare i livelli di isolamento, volenti o nolenti, ecc.)

Non esiste una soluzione magica generica a questo problema che funzioni nella pratica. Puoi trasferire la concorrenza all'applicazione, ma questo può essere molto complesso, specialmente se devi coordinarti con altri programmi in esecuzione in spazi di memoria separati.

Risposte generali per ridurre le opportunità di deadlock:

  1. Ottimizzazione delle query di base (uso corretto dell'indice) progettazione evitante di hotspot, sospensione delle transazioni per i tempi più brevi possibili ... ecc.

  2. Se possibile, impostare timeout di query ragionevoli in modo che se si verifica un deadlock, questo si auto-cancella alla scadenza del periodo di timeout.

  3. I deadlock in MSSQL sono spesso dovuti al suo modello di concorrenza di lettura predefinito, quindi è molto importante non dipendere da esso - si supponga che MVCC sia in stile Oracle in tutti i design. Utilizzare l'isolamento dello snapshot o, se possibile, il livello di isolamento READ UNCOMMITED.

Credo che il seguente utile schema di lettura / scrittura sia a prova di dead dead dato alcuni vincoli:

Vincoli:

  1. Una tabella
  2. Un indice o PK viene utilizzato per la lettura / scrittura, quindi il motore non ricorre ai blocchi della tabella.
  3. Un batch di record può essere letto usando una singola clausola SQL where.
  4. Uso della terminologia di SQL Server.

Ciclo di scrittura:

  1. Tutte le scritture in una sola "quotata in lettura" transazione.
  2. Il primo aggiornamento nella transazione è un record specifico e sempre presente  all'interno di ciascun gruppo di aggiornamento.
  3. È possibile quindi scrivere più record in qualsiasi ordine. (Sono " protetti "  dalla scrittura al primo disco).

Ciclo di lettura:

  1. Il livello di transazione di commit di lettura predefinito
  2. Nessuna transazione
  3. Leggi i record come una singola istruzione select.

I vantaggi:

  1. I cicli di scrittura secondari vengono bloccati alla scrittura del primo record fino al completamento completo della prima transazione di scrittura.
  2. Le letture vengono bloccate / messe in coda / eseguite atomicamente tra i commit di scrittura.
  3. Ottieni coerenza a livello di transazione senza ricorrere a "Serializable".

Ho bisogno che anche questo funzioni, quindi commenta / correggi !!

Come hai detto, accedere sempre alle tabelle nello stesso ordine è un ottimo modo per evitare deadlock. Inoltre, accorcia le transazioni il più possibile.

Un altro trucco interessante è quello di combinare 2 istruzioni sql in una ogni volta che puoi. Le singole dichiarazioni sono sempre transazionali. Ad esempio, usa " AGGIORNA ... SELEZIONA " o " INSERISCI ... SELEZIONA " ;, usa " @@ ERROR " e " @@ ROWCOUNT " invece di " SELEZIONA COUNT " o " IF (ESISTI ...) "

Infine, assicurati che il tuo codice chiamante sia in grado di gestire deadlock ripubblicando la query un numero di volte configurabile. A volte succede, è un comportamento normale e l'applicazione deve essere in grado di gestirlo.

Oltre alla sequenza coerente di acquisizione dei blocchi, un altro percorso è l'uso esplicito dei suggerimenti di blocco e isolamento per ridurre il tempo / le risorse sprecate involontariamente acquisendo blocchi come l'intento condiviso durante la lettura.

Qualcosa che nessuno ha menzionato (sorprendentemente), è che, per quanto riguarda SQL Server, molti problemi di blocco possono essere eliminati con il giusto set di indici di copertura per il carico di lavoro delle query di un DB. Perché? Perché può ridurre notevolmente il numero di ricerche di segnalibri nell'indice cluster di una tabella (supponendo che non sia un heap), riducendo così la contesa e il blocco.

Se si dispone di un controllo di progettazione sufficiente sulla propria app, limitare gli aggiornamenti / inserimenti a specifiche procedure memorizzate e rimuovere i privilegi di aggiornamento / inserimento dai ruoli di database utilizzati dall'app (consentire esplicitamente solo gli aggiornamenti attraverso tali procedure memorizzate).

Isola le tue connessioni al database a una classe specifica nella tua app (ogni connessione deve provenire da questa classe) e specifica che " solo query " connessioni impostano il livello di isolamento su "lettura sporca" ... l'equivalente di un (nolock) su ogni join.

In questo modo isola le attività che può causare blocchi (a specifiche procedure memorizzate) e accetta "semplici letture" fuori dal "loop di blocco".

La risposta rapida è no, non esiste una tecnica garantita.

Non vedo come è possibile rendere qualsiasi applicazione deadlock prova in generale come principio di progettazione se ha un throughput non banale. Se blocchi preventivamente tutte le risorse di cui potresti potenzialmente avere bisogno in un processo nello stesso ordine anche se non ne hai bisogno, rischi il problema più costoso in cui il secondo processo è in attesa di acquisire il primo blocco di cui ha bisogno e la tua disponibilità è influenzata. E con l'aumentare del numero di risorse nel sistema, anche i processi banali devono bloccarli tutti nello stesso ordine per evitare deadlock.

Il modo migliore per risolvere i problemi di deadlock SQL, come la maggior parte dei problemi di prestazioni e disponibilità, è guardare il carico di lavoro nel profiler e comprenderne il comportamento.

Non una risposta diretta alla tua domanda, ma spunti di riflessione:

http://en.wikipedia.org/wiki/Dining_philosophers_problem

Il problema del problema dei filosofi da pranzo " è un vecchio esperimento mentale per esaminare il problema del deadlock. Leggerlo potrebbe aiutarti a trovare una soluzione alla tua particolare circostanza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top