【问题标题】:How to get rid of deadlock in a SQL Server 2005 and C# application?如何摆脱 SQL Server 2005 和 C# 应用程序中的死锁?
【发布时间】:2013-04-10 13:31:16
【问题描述】:

我在 C# 中有一个用于 windows 服务的代码,主要负责更新数据库中表中的记录,但我的日志中总是出现很多错误,所有错误都是关于资源死锁的,

这是错误:

System.Data.SqlClient.SqlException (0x80131904):事务(进程 ID 57) 与另一个进程在锁资源上死锁,并且 被选为死锁受害者。重新运行事务。在 System.Data.SqlClient.SqlConnection.OnError(SqlException 异常, Boolean breakConnection,Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource1 完成,Int32 超时,Task& 任务, 布尔 asyncWrite) 在 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 完成,字符串方法名,布尔型 sendToPipe,Int32 超时, 布尔 asyncWrite) 在 System.Data.SqlClient.SqlCommand.ExecuteNonQuery() 在 WheelTrackListener.DataAccess.SQLDBA.ExecuteNQuery(SqlCommand cmd, Boolean isShowError, ConnectionStringType CountryCode, String deviceID, Int32 retry, String functionCallName) ClientConnectionId:e45e4cf1-a113-46b7-b9b5-dc5ee8111406

现在,我想问一下,我可以试一试或检查资源是否被锁定?如果被锁定如何等待更新直到它被释放?

这是我当前的代码:

public static int updateVehicleLastPosition(string UTCDate, string UTC_Time, 
              string NS_Indicator, string Latitude, string EWIndicator, 
              string Longtitude, string Speed, string Processed, 
              string Near_ByLocation, string Near_ByLocation_AR, 
              string Gis_dataID, string address, string ar_adress, string Device_ID)
{
        SQLMethods sql = new SQLMethods();
        SqlCommand cmd = sql.cmdUpdateVehicleLastPosition(UTCDate, UTC_Time, NS_Indicator, Latitude, EWIndicator, Longtitude, Speed, Processed, Near_ByLocation, Near_ByLocation_AR, Gis_dataID, address, ar_adress, Device_ID);
        SQLDBA sqlDBA = new SQLDBA();
        return sqlDBA.ExecuteNQuery(cmd, true, ConnectionStringType.OMN, Device_ID, 10, "updateVehicleLastPosition");
}

public SqlCommand cmdUpdateVehicleLastPosition(string UTCDate, string UTC_Time, 
                      string NS_Indicator, string Latitude, string EWIndicator, 
                      string Longtitude, string Speed, string Processed, 
                      string Near_ByLocation, string Near_ByLocation_AR, 
                      string Gis_dataID, string address, string ar_adress, 
                      string Device_ID)
 {
        string sql = "UPDATE CTS_VehicleLastPosition SET [UTCDate] = @UTCDate, [UTC_Time] = @UTC_Time, [NS_Indicator] = @NS_Indicator, [Latitude] = @Latitude, [EWIndicator] = @EWIndicator, [Longtitude] = @Longtitude, [Speed] = @Speed, [Processed] = @Processed, [Near_ByLocation] = @Near_ByLocation, [Near_ByLocation_AR] = @Near_ByLocation_AR, [Gis_dataID] = @Gis_dataID, [address] = @address, [ar_adress] = @ar_adress WHERE [Device_ID] = @Device_ID";

        SqlCommand cmd = new SqlCommand(sql);
        cmd.Parameters.AddWithValue("@UTCDate", UTCDate);
        cmd.Parameters.AddWithValue("@UTC_Time", UTC_Time);
        cmd.Parameters.AddWithValue("@NS_Indicator", NS_Indicator);
        cmd.Parameters.AddWithValue("@Latitude", Latitude);
        cmd.Parameters.AddWithValue("@EWIndicator", EWIndicator);
        cmd.Parameters.AddWithValue("@Longtitude", Longtitude);
        cmd.Parameters.AddWithValue("@Speed", Speed);
        cmd.Parameters.AddWithValue("@Processed", Processed);
        cmd.Parameters.AddWithValue("@Near_ByLocation", Near_ByLocation);
        cmd.Parameters.AddWithValue("@Near_ByLocation_AR", Near_ByLocation_AR);
        cmd.Parameters.AddWithValue("@Gis_dataID", Gis_dataID);
        cmd.Parameters.AddWithValue("@address", address);
        cmd.Parameters.AddWithValue("@ar_adress", ar_adress);
        cmd.Parameters.AddWithValue("@Device_ID", Device_ID);
        return cmd;
}

public int ExecuteNQuery(SqlCommand cmd, bool isShowError, 
                    DataAccess.ConnectionStringType CountryCode, string deviceID, 
                    int retry, string functionCallName)
{
        ConnectionManager Connection = new ConnectionManager();
        try
        {
            Connection.GetConnection(CountryCode);
            if ((Connection.con == null) || (Connection.con.State != ConnectionState.Open))
            {
                if (retry <= 0) return 0;
                else return ExecuteNQuery(cmd, isShowError, CountryCode, deviceID, retry - 1, functionCallName);
            }
            int rowsAffected = 0;
            cmd.Connection = Connection.con;
            rowsAffected = cmd.ExecuteNonQuery();
            return rowsAffected;
        }
        catch (SqlException sqlexception)
        {
            if (isShowError)
                LEAMRALogger.Logger.WriteByDate("Logs\\SQLDBA\\" + functionCallName + "\\" + String.Format("{0:dd-MM-yyyy}", DateTime.Now), "SQLDBA", "SQLDBA_ERROR", "ExecuteNQuery Function: [deviceID: " + deviceID + " | retry: " + retry + "] " + sqlexception.ToString());
        }
        catch (Exception ex)
        {
            if (isShowError)
                LEAMRALogger.Logger.WriteByDate("Logs\\SQLDBA\\" + functionCallName + "\\" + String.Format("{0:dd-MM-yyyy}", DateTime.Now), "SQLDBA", "SQLDBA_ERROR", "ExecuteNQuery Function: [deviceID: " + deviceID + " | retry: " + retry + "] " + ex.ToString());
        }
        finally
        {
            if ((Connection.con != null) && (Connection.con.State == ConnectionState.Open))
            {
                Connection.con.Close();
                Connection.con.Dispose();
            }

            GC.Collect();
        }
        if (retry <= 0) return 0;
        else return ExecuteNQuery(cmd, isShowError, CountryCode, deviceID, retry - 1, functionCallName);
}

【问题讨论】:

  • 不幸的是,UPDATES 将在更新期间锁定您的表。我会说直接在 SQL 中运行脚本并检查您的执行计划以确保它尽可能地运行。除此之外,请查看这篇关于防止死锁的文章:support.microsoft.com/kb/169960
  • 另外,您的static int updateVehicleLastPosition 方法是否可以同时被多个线程调用?如果是这样,您可能希望将其包装在 lock 语句中,因为它正在更新数据库:msdn.microsoft.com/en-us/library/c5kehkcz(v=vs.71).aspx
  • @valverij - 不,您不想在代码中使用 lock 语句。 SQL Server 有自己的锁定机制,应改为使用。

标签: c# sql-server sql-server-2005 deadlock


【解决方案1】:

我在您的代码中看不到任何明确的事务范围,所以我不知道在您进行更新时哪些锁已经到位;也不清楚您使用的是什么隔离级别。但在这种情况下最常见的情况是,在同一事务的早期,您对稍后尝试更新的同一行发出了选择(读锁)。这将导致锁升级,如果两个事务试图做同样的事情,可能会导致死锁:

  1. 事务 A:使用读锁选择
  2. 事务 B:选择读取 锁
  3. 事务 A:更新 - 想要将其读锁升级到 写锁,但必须等待事务B释放它的读 锁
  4. 事务 B:更新 - 想要将其读锁升级到 写锁,但必须等待事务A释放它的读 锁。

宾果!死锁,因为 A 和 B 都在等待对方释放他们现有的读锁,然后才能进行更新。

为防止这种情况,您需要在选择中添加更新锁定提示,例如,

select * from table with (updlock) where blah blah

这将确保您的选择使用写锁而不是读锁,这将防止并发事务之间的锁升级。

【讨论】:

    【解决方案2】:

    虽然可以检查锁,但您不能保证在发出下一条语句时其他进程还没有取出锁。按优先顺序排列的可能解决方案:

    1) 在事务中始终以相同的顺序引用表。

    2) @ShellShock 的回答

    3) 捕获死锁错误并处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多