我有一个‘数据库’类,它可以作为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;
}我能在catch块中做这个处理吗?
发布于 2008-12-02 22:18:50
首先,我要回顾一下我的SQL2000代码,并弄清楚为什么会发生这种死锁。修复这个问题可能隐藏了一个更大的问题(例如。缺少索引或查询错误)。
其次,我会检查我的架构,以确认死锁语句确实需要如此频繁地调用( 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;
}
}发布于 2011-07-14 19:13:50
基于@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;
}
});
}发布于 2008-12-02 10:22:04
如果死锁可以在数据层解决,那肯定是可行的方法。锁定提示,重新设计模块的工作方式等等。然而,NoLock并不是万能的--有时,出于事务完整性的原因,它是不可能使用的,我曾经遇到过直接(尽管复杂)读取所有相关表的数据未锁定的情况,这些情况仍然会导致其他查询上的阻塞。
不管怎样,如果你因为某种原因不能在数据层解决这个问题,那么
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;
}
}https://stackoverflow.com/questions/320636
复制相似问题