Come rilevare SqlException causata da deadlock?


92

Da un'app .NET 3.5 / C #, vorrei catturare SqlExceptionma solo se è causato da deadlock su un'istanza di SQL Server 2008.

Il messaggio di errore tipico è Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Tuttavia, non sembra essere un codice di errore documentato per questa eccezione.

Filtrare le eccezioni in base alla presenza della parola chiave deadlock nel loro messaggio sembra un modo molto brutto per ottenere questo comportamento. Qualcuno conosce il modo giusto per farlo?


3
Ho (finalmente) trovato la documentazione per il codice di errore: msdn.microsoft.com/en-us/library/aa337376.aspx . Puoi anche trovarlo tramite SQL Server stesso:select * from master.dbo.sysmessages where error=1205
Martin McNulty

Risposte:


154

Il codice di errore specifico di Microsft SQL Server per un deadlock è 1205, quindi è necessario gestire SqlException e verificarlo. Quindi, ad esempio, se per tutti gli altri tipi di SqlException vuoi che la bolla faccia l'eccezione:

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

Oppure, utilizzando il filtro delle eccezioni disponibile in C # 6

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

Una cosa utile da fare per trovare il codice di errore SQL effettivo per un dato messaggio, è cercare in sys.messages in SQL Server.

per esempio

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

Un modo alternativo per gestire i deadlock (da SQL Server 2005 e versioni successive) è farlo all'interno di una stored procedure utilizzando il supporto TRY ... CATCH:

BEGIN TRY
    -- some sql statements
END TRY
BEGIN CATCH
    IF (ERROR_NUMBER() = 1205)
        -- is a deadlock
    ELSE
        -- is not a deadlock
END CATCH

C'è un esempio completo qui in MSDN di come implementare la logica di ripetizione dei tentativi di deadlock esclusivamente all'interno di SQL.


2
Nota che i codici di errore sono specifici del fornitore, quindi 1205 è un deadlock per SQL Server, ma potrebbe essere diverso per Oracle, MySQL, ecc.
brianmearns

3
A seconda del livello dati, SqlExceptionpotrebbe essere inserito in un altro. Quindi potrebbe essere necessario rilevare qualsiasi tipo di eccezione e controllarli, quindi, se non sono direttamente un'eccezione deadlock, controllarli ricorsivamente InnerException.
Frédéric

46

Poiché suppongo che tu voglia rilevare i deadlock, per poter ritentare l'operazione fallita, mi piace avvisarti per un piccolo trucco. Spero che mi scuserai per essere un po 'fuori tema qui.

Un deadlock rilevato dal database eseguirà effettivamente il rollback della transazione in cui era in esecuzione (se presente), mentre la connessione viene mantenuta aperta in .NET. Riprovare tale operazione (nella stessa connessione) significa che verrà eseguita in un contesto senza transazioni e questo potrebbe portare al danneggiamento dei dati.

È importante esserne consapevoli. È meglio considerare la connessione completa condannata in caso di errore causato da SQL. Il nuovo tentativo dell'operazione può essere eseguito solo al livello in cui è definita la transazione (ricreando quella transazione e la sua connessione).

Quindi, quando ripeti un'operazione non riuscita, assicurati di aprire una connessione completamente nuova e di avviare una nuova transazione.


4
Perché hai bisogno di una connessione completamente nuova? Ho pubblicato una domanda su questa risposta qui .
Sam

3

Ecco un modo C # 6 per rilevare i deadlock.

try
{
    //todo: Execute SQL. 
    //IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
    //todo: Retry SQL
}

Assicurati che questo try..catch circondi l'intera transazione. Secondo @Steven (vedere la sua risposta per i dettagli), quando il comando sql fallisce a causa del deadlock, provoca il rollback della transazione e, se non si ricrea la transazione, il nuovo tentativo verrà eseguito al di fuori del contesto di la transazione e può causare incongruenze nei dati.

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.