我的 .NET 应用程序的事件日志显示,它在从 Sql Server 读取数据时偶尔会出现死锁。这种情况通常非常罕见,因为我们已经优化了查询以避免死锁,但有时仍然会发生。过去,我们在调用ExecuteReader
函数在我们的SqlCommand
实例。为了解决这个问题,我们添加了重试代码以再次运行查询,如下所示:
//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;
}
}
这对于在初始查询执行期间发生死锁的情况非常有效,但现在我们在使用迭代结果时遇到死锁Read()
返回的函数SqlDataReader
.
同样,我并不关心优化查询,而只是尝试在发生死锁的罕见情况下进行恢复。我正在考虑使用类似的重试过程。我可以创建自己的类继承自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
重试之间给数据库时间来摆脱死锁状态,或者这是否足够。这种情况不容易重现,因此我想在修改任何代码之前确定这一点。
EDIT:
根据要求,提供一些有关我的情况的更多信息:在一种情况下,我有一个执行查询的进程,该查询加载需要更新的记录 ID 列表。然后我使用 id 列表进行迭代Read
函数并对该记录运行更新过程,这最终将更新数据库中该记录的值。 (不,无法在初始查询中执行更新,返回的每条记录都会发生许多其他事情)。这段代码已经运行良好一段时间了,但是我们正在为每条记录运行相当多的代码,所以我可以想象其中一个进程正在读取的初始表上创建一个锁。
经过一番思考,Scottie 建议使用数据结构来存储结果,这可能会解决这种情况。我可以将返回的 id 存储在List<int>
并循环遍历它。这样就可以立即删除行上的锁。
但是,我仍然有兴趣知道是否有一种通用方法可以从读取死锁中恢复。