如何在C#中使用ADO获得高效的Sql Server死锁处理?
我有一个类’数据库’,作为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; } }
如果遇到死锁问题,最好查看SQL代码在做什么。 例如,如果您具有可序列化的隔离级别(或rdbms中的等效项),则很容易创建锁定升级死锁 – 并且可以通过几种方式进行缓解,例如重新排序查询,或者(在SQL Server中)至少使用(UPDLOCK)更早地进行写锁定(因此您不会获得竞争读锁)。
重新尝试将会混合…例如,如果您在TransactionScope中,它可能已经中止。 但只是在纯粹层面 – 如果我在与数据库谈话时遇到问题我希望我的代码恐慌,并且恐慌早期……在这种特殊情况下重新尝试似乎有点笨拙。
上述就是C#学习教程:如何在C#中使用ADO获得高效的Sql Server死锁处理?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/998601.html