교착 상태로 인한 SqlException을 잡는 방법은 무엇입니까?


92

닷넷 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.

그러나이 예외에 대해 문서화 된 오류 코드 는 아닌 것 같습니다 .

메시지에서 교착 상태 키워드 의 존재에 대한 예외를 필터링 하는 것은이 동작을 달성하는 매우 추악한 방법으로 보입니다. 누군가이 올바른 방법을 알고 있습니까?


3
나는 (마침내) 오류 코드에 대한 설명서를 찾았습니다 : msdn.microsoft.com/en-us/library/aa337376.aspx . SQL Server 자체를 통해서도 찾을 수 있습니다.select * from master.dbo.sysmessages where error=1205
Martin McNulty 2013

답변:


154

교착 상태에 대한 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

여기 MSDN에는 순수하게 SQL 내에서 교착 상태 재시도 논리를 구현하는 방법에 대한 전체 예제가 있습니다 .


2
오류 코드는 1205 그래서는 SQL Server의 교착 상태이며, 벤더 고유하지만 등 오라클, MySQL은, 다를 수 있습니다 참고
brianmearns

3
데이터 레이어에 따라 SqlException다른 레이어 로 래핑 될 수 있습니다. 따라서 예외 종류를 잡아서 확인해야 할 수도 있습니다. 그런 다음 직접 교착 상태 예외가 아닌 경우 재귀 적으로 InnerException.
Frédéric

46

교착 상태를 감지하고 실패한 작업을 다시 시도 할 수 있기를 원하기 때문에 약간의 문제에 대해 경고하고 싶습니다. 여기서 주제에서 약간 벗어난 것에 대해 실례합니다.

데이터베이스에서 감지 된 교착 상태는 연결이 .NET에서 열린 상태로 유지되는 동안 실행중인 트랜잭션 (있는 경우)을 효과적으로 롤백합니다. 동일한 연결에서 해당 작업을 재 시도하면 트랜잭션이없는 컨텍스트에서 실행되고 이로 인해 데이터가 손상 될 수 있습니다.

이것을 인식하는 것이 중요합니다. SQL로 인한 실패의 경우 완전한 연결을 고려하는 것이 가장 좋습니다. 작업 재 시도는 트랜잭션이 정의 된 수준에서만 수행 할 수 있습니다 (해당 트랜잭션과 연결을 다시 생성하여).

따라서 실패한 작업을 다시 시도 할 때 완전히 새로운 연결을 열고 새 트랜잭션을 시작했는지 확인하십시오.


4
완전히 새로운 연결이 필요한 이유는 무엇입니까? 이 답변에 대한 질문을 여기 에 게시했습니다 .
Sam

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 명령이 실패하면 트랜잭션이 롤백되고 트랜잭션을 다시 생성하지 않으면 재 시도가 컨텍스트 외부에서 실행됩니다. 데이터 불일치를 초래할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.