Vic*_*ues 21 c# sql-server ado.net deadlock .net-2.0
我有一个类'数据库',作为ADO.net的包装器.例如,当我需要执行一个过程时,我调用Database.ExecuteProcedure(procedureName,parametersAndItsValues).
我们遇到了SQL Server 2000中死锁情况的严重问题.我们团队的一部分正在处理sql代码和事务以最小化这些事件,但我正在考虑使这个数据库类能够抵御死锁情况.
我们希望死锁受害者可能在经过一段时间的延迟后重试,但我不知道是否可能.这是我们使用的方法的代码:
public int ExecuteQuery(string query)
{
int rows = 0;
try
{
Command.Connection = Connection;
Command.CommandType = CommandType.Text;
if(DatabaseType != enumDatabaseType.ORACLE)
Command.CommandText = query;
else
Command.CommandText ="BEGIN " + query + " END;";
if (DatabaseType != enumDatabaseType.SQLCOMPACT)
Command.CommandTimeout = Connection.ConnectionTimeout;
if (Connection.State == ConnectionState.Closed)
Connection.Open();
rows = Command.ExecuteNonQuery();
}
catch (Exception exp)
{
//Could I add here any code to handle it?
throw new Exception(exp.Message);
}
finally
{
if (Command.Transaction == null)
{
Connection.Close();
_connection.Dispose();
_connection = null;
Command.Dispose();
Command = null;
}
}
return rows;
}
Run Code Online (Sandbox Code Playgroud)
我可以在catch块内进行此处理吗?
Sam*_*ron 36
首先,我将查看我的SQL 2000代码并深入了解发生此死锁的原因.解决这个问题可能隐藏了一个更大的问题(例如,缺少索引或错误的查询).
其次,我会检查我的架构,以确认经常需要调用死锁语句(select count(*) from bob
必须每秒调用100次?).
但是,如果您确实需要一些死锁支持并且在SQL或体系结构中没有错误,请尝试以下几行.(注意:我必须将此技术用于每秒支持数千个查询的系统,并且很少会遇到死锁)
int retryCount = 3;
bool success = false;
while (retryCount > 0 && !success)
{
try
{
// your sql here
success = true;
}
catch (SqlException exception)
{
if (exception.Number != 1205)
{
// a sql exception that is not a deadlock
throw;
}
// Add delay here if you wish.
retryCount--;
if (retryCount == 0) throw;
}
}
Run Code Online (Sandbox Code Playgroud)
Nei*_*eil 21
基于@Sam的响应,我提出了一个通用的重试包装方法:
private static T Retry<T>(Func<T> func)
{
int count = 3;
TimeSpan delay = TimeSpan.FromSeconds(5);
while (true)
{
try
{
return func();
}
catch(SqlException e)
{
--count;
if (count <= 0) throw;
if (e.Number == 1205)
_log.Debug("Deadlock, retrying", e);
else if (e.Number == -2)
_log.Debug("Timeout, retrying", e);
else
throw;
Thread.Sleep(delay);
}
}
}
private static void Retry(Action action)
{
Retry(() => { action(); return true; });
}
// Example usage
protected static void Execute(string connectionString, string commandString)
{
_log.DebugFormat("SQL Execute \"{0}\" on {1}", commandString, connectionString);
Retry(() => {
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(commandString, connection))
command.ExecuteNonQuery();
});
}
protected static T GetValue<T>(string connectionString, string commandString)
{
_log.DebugFormat("SQL Scalar Query \"{0}\" on {1}", commandString, connectionString);
return Retry(() => {
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(commandString, connection))
{
object value = command.ExecuteScalar();
if (value is DBNull) return default(T);
return (T) value;
}
});
}
Run Code Online (Sandbox Code Playgroud)
小智 5
如果可以在数据层解决死锁,那肯定是要走的路.锁定提示,重新设计模块的工作方式等等.NoLock不是灵丹妙药 - 有时由于事务完整性的原因而无法使用,而且我遇到了直接(尽管很复杂)数据读取的情况,所有相关表NoLock'd仍会导致其他查询出现阻塞.
无论如何 - 如果由于某种原因你无法在数据层解决它,那怎么样
bool OK = false;
Random Rnd = new Random();
while(!OK)
{
try
{
rows = Command.ExecuteNonQuery();
OK = true;
}
catch(Exception exDead)
{
if(exDead.Message.ToLower().Contains("deadlock"))
System.Threading.Thread.Sleep(Rnd.Next(1000, 5000));
else
throw exDead;
}
}
Run Code Online (Sandbox Code Playgroud)