我有一个类'数据库',作为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块内进行此处理吗?
首先,我将查看我的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; } }
基于@Sam的响应,我提出了一个通用的重试包装方法:
private static T Retry(Func 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 (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; } }); }
如果可以在数据层解决死锁,那肯定是要走的路.锁定提示,重新设计模块的工作方式等等.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; } }