2012-06-19 19 views
8

He desarrollado una aplicación de supervisión. Entonces, he usado una función Timer para verificar algunos valores en una tabla SQL.La transacción (ID de proceso 84) se ha bloqueado en los recursos de bloqueo con otro proceso y se ha elegido como la víctima de interbloqueo

aunque hay tantos función que da un error de seguimiento para una función llamada 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> 

Ésta es la implementación de la función

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(); 


      } 


     } 
    } 

Gracias de antemano

+0

Es posible que desee tener en cuenta las sugerencias hechas en esta respuesta: http://stackoverflow.com/questions/2382410/sql-server-deadlock-fix-force-join-order-or-automatically-retry. Hemos implementado con éxito reintentos de búsqueda si la consulta original está en punto muerto. – dash

+0

Además, ¿cuántas entradas de registro está escribiendo? Si escribe mucho, es posible que simplemente esté impidiendo el SELECT con una gran cantidad de INSERTOS. – dash

+0

Por esta aplicación, nada está escrito en esas tablas, pero otro Software escribe datos en esas tablas. –

Respuesta

13

Puede que desee consultar este question para obtener más sugerencias útiles.

Utilizo el siguiente patrón para los reintentos de la base de datos; en este caso, devolvemos una DataTable pero el patrón es el mismo independientemente; detecta un SqlDeadlock o tiempo de espera basado en la SqlException Number, y vuelve a intentar, hasta un número máximo de n veces.

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

Espero que estés controlando esto. Si, por ejemplo, obtiene un punto muerto, vuelva a intentar cada millón de consultas, o por mes, entonces está bien. Si obtienes 1 por minuto o cualquier otra consulta, entonces estás en problemas y debes solucionar la causa en lugar de solucionar el problema ... – MatBailie

+0

@Dems A veces te encuentras en un punto muerto pero no quieres que tu aplicación muera porque de eso; reintentar le compra el tiempo que necesita para investigar la causa raíz del problema; tenga en cuenta que sugiero leer el primer enlace :-) A veces también obtiene un punto muerto o tiempo de espera simplemente porque el sistema está ocupado. – dash

+0

No digo que no haga esto, solo digo que lo vigile. Como dije, si no es frecuente, entonces estás bien. Pero si es regular, entonces tienes un problema y lo has forzado a una falla silenciosa: bueno para la aplicación en ese momento, pero significa que necesitas una forma de controlar si sucede con tanta frecuencia que realmente necesitas dirigirte. la causa subyacente y no solo sus efectos. Si no supervisas, entonces ni siquiera sabes que hay algo que investigar. – MatBailie

3

Su consulta se estanca con otra consulta. La otra consulta probablemente sea una consulta insert, update o delete, ya que select por sí solo no tiende a un punto muerto.

Si no se preocupan demasiado por la consistencia, se puede utilizar el with (nolock) pista:

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

Eso hará que su consulta no colocar cerraduras. Una consulta sin bloqueos no causará interbloqueos. La desventaja es que puede devolver datos inconsistentes, cuando se ejecuta al mismo tiempo que una consulta de modificación.

+0

Tenga en cuenta que si el bloqueo se ha escalado a un bloqueo de tabla, esto no funcionará; vea http://msdn.microsoft.com/en-us/library/ms187373.aspx. – dash

+0

@dash: Ese artículo se aplica solo a DDL, como 'alter table', que adquiere un bloqueo Sch-M (modificación de esquema). Un 'con (nolock)' leerá felizmente un 'con (tablockx)' – Andomar

+0

"El soporte para el uso de las sugerencias READUNCOMMITTED y NOLOCK en la cláusula FROM que se aplican a la tabla de destino de una instrucción UPDATE o DELETE se eliminará en una versión futura de SQL Server. Evite utilizar estos consejos en este contexto en el nuevo trabajo de desarrollo, y planee modificar las aplicaciones que actualmente los usan. " también está en eso. No estoy 100% seguro de que leerá una transacción en todos los casos; He tenido situaciones en las que nolock no ha funcionado porque ha habido un bloqueo de mesa. – dash

Cuestiones relacionadas