2012-06-19 14 views
8

Ho sviluppato un'applicazione di monitoraggio. Quindi ho usato una funzione Timer per controllare alcuni valori in una tabella SQL.La transazione (ID processo 84) era bloccata su risorse di blocco con un altro processo ed è stata scelta come vittima del deadlock

se ci sono tanti funzione dà un messaggio di errore per una funzione chiamata getLogEntry()

message>Transaction (Process ID 84) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.</message> 
<innerMessage> 
</innerMessage> 
<source>.Net SqlClient Data Provider</source> 
<stackTrace>at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) 
    at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) 
    at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 
    at System.Data.SqlClient.SqlDataReader.HasMoreRows() 
    at System.Data.SqlClient.SqlDataReader.ReadInternal(Boolean setTimeout) 
    at ShiftAlertSystem.DBAccess.getLogEntry(Int32 nEventLogIdn, connections cn)</stackTrace> 
    <createdAt>2012/06/18 13:10:47</createdAt> 

Questa è l'implementazione della funzione

public LogEntry getLogEntry(int nEventLogIdn, connections cn) 
    { 
     lock (_objLock) 
     { 
      LogEntry lgEntObj = new LogEntry(); 
      SqlConnection NewCon3 = new SqlConnection(); 
      SqlCommand newCmd2 = null; 
      SqlDataReader dr = null; 

      try 
      { 


       string connectString; 
       // Configuration config = ConfigurationManager.u 
       string DataSource = cryptIT.Decrypt(cn.DataSource_bio); 
       string initialCatalog = cryptIT.Decrypt(cn.InitialCatalog_bio); 
       string user = cryptIT.Decrypt(cn.user_bio); 
       string password = cryptIT.Decrypt(cn.password_bio); 
       bool intergratedSecurity = cn.IntegratedSecurity_bio; 

       if (intergratedSecurity) 
       { 
        connectString = "Data Source=" + DataSource + ";Initial Catalog=" + initialCatalog + ";Integrated Security=True"; 
       } 
       else 
       { 
        connectString = "Data Source=" + DataSource + ";Initial Catalog=" + initialCatalog + ";User ID=" + user + ";Password=" + password; 
       } 

       NewCon3 = new SqlConnection(connectString); 
       NewCon3.Open(); 



       newCmd2 = NewCon3.CreateCommand(); 
       newCmd2.Connection = NewCon3; 
       newCmd2.CommandType = CommandType.Text; 
       newCmd2.CommandText = @" 
           SELECT [nUserID] 
             ,[sUserName] 
             ,dateadd(s,[nDateTime],'1970/1/1') AS LogDateTime 
             ,[nEventIdn] 
             ,[nTNAEvent] 
             ,[TB_READER].[nReaderIdn] 
             ,[sName] 
           FROM 
             [TB_EVENT_LOG] 
             ,[TB_USER] 
             ,[TB_READER] 
           WHERE 

             [nEventLogIdn] = " + nEventLogIdn + 
             @" AND 
             [TB_EVENT_LOG].[nUserID] = [TB_USER].[sUserID] 
             AND 
             [nFlag]= 1 
             AND 
             [TB_EVENT_LOG].[nReaderIdn]=[TB_READER].[nReaderIdn]" 
             ; 
       dr = newCmd2.ExecuteReader(); 

       if (dr != null && dr.Read()) 
       { 
        lgEntObj.nUserID = dr.GetInt32(0); 
        lgEntObj.nUserName = dr.GetString(1); 
        lgEntObj.LogDateTime = dr.GetDateTime(2); 
        lgEntObj.nEventIdn = dr.GetInt32(3); 
        lgEntObj.nTNAEvent = dr.GetInt16(4); 
        lgEntObj.nReaderIdn = dr.GetInt32(5); 
        lgEntObj.sName = dr.GetString(6); 
       } 
       dr.Close(); 
       newCmd2.Dispose(); 
       // NewCon.Close(); 
       NewCon3.Close(); 

       return lgEntObj; 
      } 
      catch (Exception exc) 
      { 
       CenUtility.ErrorLog.CreateLog(exc); 
       return null; 
      } 

      finally 
      { 
       if (dr != null) 
        dr.Close(); 

       if(newCmd2 != null) 
        newCmd2.Dispose(); 


        NewCon3.Close(); 


      } 


     } 
    } 

Grazie in anticipo

+0

Si potrebbe prendere in considerazione le proposte avanzate in questa risposta: http://stackoverflow.com/questions/2382410/sql-server-deadlock-fix-force-join-order-or-automatically-retry. Abbiamo implementato con successo i tentativi di query se la query originale è deadlock. – dash

+0

Inoltre, quante voci di registro stai scrivendo? Se stai scrivendo molto, potrebbe essere che stai semplicemente impedendo la SELEZIONE con un gran numero di INSERTI. – dash

+0

Con questa applicazione non viene scritto nulla in quelle tabelle, ma un altro software scrive i dati in quelle tabelle. –

risposta

13

È possibile fare riferimento a questo question per ulteriori suggerimenti utili.

Io uso il seguente modello per tentativi di database; in questo caso, restituiamo un DataTable ma il pattern è lo stesso indipendentemente; si rileva un SqlDeadlock o Timeout in base a SqlException Number e si riprova, fino ad un numero massimo di n volte.

public DataTable DoSomeSql(int retryCount = 1) 
    { 
     try 
     { 
      //Run Stored Proc/Adhoc SQL here 

     } 
     catch (SqlException sqlEx) 
     { 
      if (retryCount == MAX_RETRY_COUNT) //5, 7, Whatever 
      { 
       log.Error("Unable to DoSomeSql, reached maximum number of retries."); 
       throw; 
      } 

      switch (sqlEx.Number) 
      { 
       case DBConstants.SQL_DEADLOCK_ERROR_CODE: //1205 
        log.Warn("DoSomeSql was deadlocked, will try again."); 
        break; 
       case DBConstants.SQL_TIMEOUT_ERROR_CODE: //-2 
        log.Warn("DoSomeSql was timedout, will try again."); 
        break; 
       default: 
        log.WarnFormat(buf.ToString(), sqlEx); 
        break; 
      } 

      System.Threading.Thread.Sleep(1000); //Can also use Math.Rand for a random interval of time 
      return DoSomeSql(asOfDate, ++retryCount); 
     } 
    } 
+3

Spero che tu stia monitorando questo. Se, ad esempio, ottieni un errore critico ogni tre milioni di query o al mese, allora stai bene. Se ottieni 1 ogni minuto, o ogni altra query, allora sei nei guai e dovresti risolvere la causa piuttosto che aggirarla ... – MatBailie

+0

@Dems A volte ottieni un deadlock ma non vuoi che la tua applicazione muoia perché di esso; riprovare ti fa guadagnare il tempo necessario per investigare la causa principale del problema; si noti che suggerisco di leggere il primo link :-) A volte si ottiene anche un deadlock o timeout solo perché il sistema è occupato. – dash

+0

Non sto dicendo di non farlo, sto solo dicendo di monitorarlo. Come ho detto, se non è frequente, allora sei bravo. Ma se è regolare, allora hai un problema e lo hai forzato in un errore silenzioso - Buono per l'app in quel momento, ma significa che hai bisogno di un modo per monitorare se succede così spesso che devi effettivamente indirizzarti la causa sottostante e non solo i suoi effetti. Se non controlli, non sai nemmeno che c'è qualcosa da investigare. – MatBailie

3

La tua query ha un deadlock con un'altra query. L'altra query è molto probabilmente una query insert, update o delete, dal momento che select da solo non tende a deadlock.

Se non si cura troppo di coerenza, è possibile utilizzare il with (nolock) suggerimento:

FROM 
    [TB_EVENT_LOG] with (nolock) 
    ,[TB_USER] with (nolock) 
    ,[TB_READER] with (nolock) 

che farà sì che la vostra richiesta di non luogo serrature. Una query senza blocchi non causerà deadlock. Lo svantaggio è che potrebbe restituire dati incoerenti, quando viene eseguito contemporaneamente a una query di modifica.

+0

Si noti che se il blocco si è inoltrato a un Blocco tabella, ciò non funzionerà; vedere http://msdn.microsoft.com/en-us/library/ms187373.aspx. – dash

+0

@dash: Questo articolo si applica solo al DDL, come 'alter table', che acquisisce un blocco Sch-M (modifica dello schema). A 'with (nolock)' leggerà felicemente un 'con (tablockx)' – Andomar

+0

"Il supporto per l'uso dei comandi READUNCOMMITTED e NOLOCK nella clausola FROM che si applicano alla tabella di destinazione di un'istruzione UPDATE o DELETE verrà rimosso in un versione futura di SQL Server. Evitare di utilizzare questi suggerimenti in questo contesto in un nuovo lavoro di sviluppo e pianificare di modificare le applicazioni che attualmente li utilizzano. " è anche in questo. Non sono sicuro al 100% che leggerà attraverso una transazione in tutti i casi; Ho avuto situazioni in cui nolock non ha funzionato perché è stato installato un tablelock. – dash