如何捕获死锁引起的SqlException?

Joa*_*rel 88 .net deadlock sqlexception try-catch sql-server-2008

从.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.

但是,对于此异常,它似乎不是一个记录错误代码.

针对其消息中存在死锁关键字而过滤异常似乎是实现此行为的一种非常难看的方式.有人知道这样做的正确方法吗?

Ada*_*Dev 146

针对死锁的Microsft SQL Server特定错误代码是1205,因此您需要处理SqlException并检查它.因此,例如,如果对于所有其他类型的SqlException,您希望气泡异常:

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}
Run Code Online (Sandbox Code Playgroud)

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

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}
Run Code Online (Sandbox Code Playgroud)

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

例如

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033
Run Code Online (Sandbox Code Playgroud)

处理死锁(来自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
Run Code Online (Sandbox Code Playgroud)

有一个完整的例子这里在如何SQL内纯粹实施僵局重试逻辑MSDN.

  • 根据数据层,`SqlException`可能包含在另一个中.所以我们可能需要捕获任何异常类并检查它们,如果它们不是直接死锁异常,则递归检查它们的`InnerException`. (3认同)
  • 请注意,错误代码是特定于供应商的,因此1205是SQL Server的死锁,但对于Oracle,MySQL等可能会有所不同. (2认同)

Ste*_*ven 43

因为我想你可能想要检测死锁,为了能够重试失败的操作,我想警告你一点点陷阱.我希望你能原谅我在这里有点偏离主题.

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

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

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

  • 为什么需要一个全新的连接?我已经发布了一个关于这个答案的问题[这里](http://stackoverflow.com/q/19108680/238753). (4认同)

Bri*_*ian 7

这是检测死锁的 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
}
Run Code Online (Sandbox Code Playgroud)

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