如何捕获死锁引起的SqlException?


92

我想从.NET 3.5 / C#应用程序捕获,SqlException前提是它是由 SQL Server 2008实例上的死锁引起的

典型的错误信息是 Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

但是,它似乎不是针对此异常的已记录错误代码

针对他们的消息中存在的Deadlock关键字过滤异常似乎是实现此行为的非常难看的方法。有人知道这样做的正确方法吗?


3
我(最后)找到了错误代码的文档:msdn.microsoft.com/en-us/library/aa337376.aspx。您也可以通过SQL Server本身找到它:select * from master.dbo.sysmessages where error=1205
Martin McNulty 2013年

Answers:


153

死锁的特定于Microsft SQL Server的错误代码是1205,因此您需要处理SqlException并进行检查。因此,例如,如果对于所有其他类型的SqlException,您想要气泡将异常向上:

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

或者,使用C#6中提供的异常过滤

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

查找给定消息的实际SQL错误代码的一件方便的事情是在SQL Server中查找sys.messages。

例如

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

处理死锁(来自SQL Server 2005及更高版本)的另一种方法是,使用TRY ... CATCH支持在存储过程中完成死锁:

BEGIN TRY
    -- some sql statements
END TRY
BEGIN CATCH
    IF (ERROR_NUMBER() = 1205)
        -- is a deadlock
    ELSE
        -- is not a deadlock
END CATCH

有一个完整的例子这里在如何纯粹的内SQL执行死锁重试逻辑MSDN。


2
请记下错误代码是供应商特定的,所以1205是SQL Server的一个僵局,但它可能是甲骨文的MySQL等不同
brianmearns

3
根据数据层,SqlException可以将其包装在另一层中。因此,我们可能需要捕获任何异常种类并进行检查,如果它们不是直接的死锁异常,则递归检查它们InnerException
弗雷德里克

46

因为我想您可能想检测死锁,以便能够重试失败的操作,所以我想警告您一点陷阱。希望您能在这里谈不上话题。

当连接在.NET中保持打开状态时,数据库检测到的死锁将有效回滚您正在其中运行的事务(如果有)。重试该操作(在同一连接中),意味着将在无事务的上下文中执行该操作,这可能导致数据损坏。

意识到这一点很重要。最好考虑在SQL导致失败的情况下注定的完整连接。重试该操作只能在定义事务的级别上进行(通过重新创建该事务及其连接)。

因此,当您尝试失败的操作时,请确保您打开一个全新的连接并开始新的事务。


4
为什么需要一个全新的连接?我已经发布了关于这个问题的答案在这里
2013年

3

这是检测死锁的C#6方法。

try
{
    //todo: Execute SQL. 
    //IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
    //todo: Retry SQL
}

确保此try..catch围绕您的整个交易。根据@Steven(有关详细信息,请参阅他的答案),当sql命令由于死锁而失败时,它将导致回滚事务,并且,如果不重新创建事务,则重试将在以下上下文之外执行交易,并可能导致数据不一致。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.