【发布时间】:2010-11-10 15:38:49
【问题描述】:
我的 .NET 应用程序的事件日志显示它在从 Sql Server 读取时偶尔会死锁。这通常很少见,因为我们已经优化了查询以避免死锁,但有时仍然会发生。过去,我们在SqlCommand 实例上调用ExecuteReader 函数时发生了一些死锁。为了解决这个问题,我们添加了重试代码来简单地再次运行查询,如下所示:
//try to run the query five times if a deadlock happends
int DeadLockRetry = 5;
while (DeadLockRetry > 0)
{
try
{
return dbCommand.ExecuteReader();
}
catch (SqlException err)
{
//throw an exception if the error is not a deadlock or the deadlock still exists after 5 tries
if (err.Number != 1205 || --DeadLockRetry == 0)
throw;
}
}
这对于在初始查询执行期间发生死锁的情况非常有效,但现在我们在使用返回的SqlDataReader 上的Read() 函数迭代结果时遇到死锁。
同样,我并不关心优化查询,而只是在发生死锁的极少数情况下尝试恢复。我正在考虑使用类似的重试过程。我可以创建自己的类,该类继承自SqlDataReader,它只是用重试代码覆盖Read 函数。像这样:
public class MyDataReader : SqlDataReader
{
public override bool Read()
{
int DeadLockRetry = 5;
while (DeadLockRetry > 0)
{
try
{
return base.Read();
}
catch (SqlException ex)
{
if (ex.ErrorCode != 1205 || --DeadLockRetry == 0)
throw;
}
}
return false;
}
}
这是正确的方法吗?我想确保阅读器中不会跳过记录。死锁后重试Read 会跳过任何行吗?另外,我是否应该在重试之间调用Thread.Sleep 以让数据库有时间摆脱死锁状态,或者这样就足够了。这种情况不容易重现,所以我想在修改任何代码之前确定它。
编辑:
根据要求,提供有关我的情况的更多信息:在一种情况下,我有一个执行查询的进程,该查询加载需要更新的记录 ID 列表。然后我使用Read 函数遍历该ID 列表并对该记录运行更新过程,最终将更新数据库中该记录的值。 (不,没有办法在初始查询中执行更新,返回的每条记录都会发生许多其他事情)。这段代码已经运行了一段时间,但是我们为每条记录运行了相当多的代码,所以我可以想象其中一个过程是在正在读取的初始表上创建一个锁。
经过一番思考,Scottie 提出的使用数据结构存储结果的建议可能会解决这种情况。我可以将返回的 ID 存储在 List<int> 中并循环遍历它。这样行上的锁就可以立即解除。
但是,我仍然想知道是否有一种通用的方法可以从读取死锁中恢复。
【问题讨论】:
-
你能提供更多关于你的数据库使用的信息吗?读取数据时出现死锁很奇怪。这意味着您的代码已经为其他数据持有一些锁,并且另一个事务已锁定您正在尝试读取的内容并正在等待您之前访问过的数据。你可以在另一个事务上调用 ExecuteReader 吗?
标签: c# .net sql-server deadlock