Làm thế nào để bắt SqlException gây ra bởi deadlock?


92

Từ ứng dụng .NET 3.5 / C #, tôi muốn bắt SqlExceptionnhưng chỉ khi nó là do bế tắc trên phiên bản SQL Server 2008.

Thông báo lỗi điển hình là Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Tuy nhiên, nó dường như không phải là một mã lỗi được ghi lại cho ngoại lệ này.

Lọc ngoại lệ chống lại sự hiện diện của từ khóa deadlock trong thư của họ có vẻ là một cách rất xấu để đạt được hành vi này. Có ai đó biết đúng cách để làm điều này?


3
Tôi (cuối cùng) đã tìm thấy tài liệu cho mã lỗi: msdn.microsoft.com/en-us/library/aa337376.aspx . Bạn cũng có thể tìm thấy điều này thông qua chính SQL Server:select * from master.dbo.sysmessages where error=1205
Martin McNulty

Câu trả lời:


154

Mã lỗi Microsft SQL Server cụ thể cho một bế tắc là 1205, vì vậy bạn cần phải xử lý SqlException và kiểm tra điều đó. Vì vậy, ví dụ: nếu đối với tất cả các loại SqlException khác, bạn muốn bong bóng có ngoại lệ:

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

Hoặc, sử dụng lọc ngoại lệ có sẵn trong C # 6

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

Một điều hữu ích cần làm để tìm mã lỗi SQL thực tế cho một thông báo nhất định, là tìm trong sys.messages trong SQL Server.

ví dụ

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

Một cách thay thế để xử lý các deadlock (từ SQL Server 2005 trở lên), là thực hiện nó trong một quy trình được lưu trữ bằng cách sử dụng hỗ trợ 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

Có một ví dụ đầy đủ ở đây trong MSDN về cách triển khai logic thử lại deadlock hoàn toàn trong SQL.


2
Lưu ý các mã lỗi là nhà cung cấp cụ thể, vì vậy 1205 là một bế tắc cho SQL Server, nhưng nó có thể khác nhau cho Oracle, MySQL, vv
brianmearns

3
Tùy thuộc vào lớp dữ liệu, lớp SqlExceptioncó thể được bọc trong một lớp khác. Vì vậy, chúng ta có thể cần phải bắt bất kỳ loại ngoại lệ nào và kiểm tra chúng sau đó, nếu chúng không trực tiếp là ngoại lệ deadlock, hãy kiểm tra đệ quy chúng InnerException.
Frédéric

46

Bởi vì tôi cho rằng bạn có thể muốn phát hiện các deadlock, để có thể thử lại thao tác bị lỗi, tôi muốn cảnh báo bạn một chút. Tôi hy vọng bạn sẽ thứ lỗi vì tôi hơi lạc đề ở đây.

Cơ sở dữ liệu phát hiện thấy một bế tắc sẽ khôi phục hiệu quả giao dịch mà bạn đang chạy (nếu có), trong khi kết nối vẫn mở trong .NET. Thử lại thao tác đó (trong cùng một kết nối), nghĩa là nó sẽ được thực thi trong ngữ cảnh không có giao dịch và điều này có thể dẫn đến hỏng dữ liệu.

Điều quan trọng là phải nhận thức được điều này. Tốt nhất là bạn nên xem xét kết nối hoàn chỉnh trong trường hợp lỗi do SQL gây ra. Việc thử lại thao tác chỉ có thể được thực hiện ở cấp độ mà giao dịch được xác định (bằng cách tạo lại giao dịch đó và kết nối của nó).

Vì vậy, khi bạn đang thử lại một thao tác không thành công, hãy đảm bảo rằng bạn mở một kết nối hoàn toàn mới và bắt đầu một giao dịch mới.


4
Tại sao bạn cần một kết nối hoàn toàn mới? Tôi đã đăng một câu hỏi về câu trả lời này ở đây .
Sam

3

Đây là một cách C # 6 để phát hiện các bế tắc.

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
}

Đảm bảo rằng thử..catch này bao quanh toàn bộ giao dịch của bạn. Theo @Steven (xem câu trả lời của anh ấy để biết chi tiết), khi lệnh sql không thành công do bế tắc, nó khiến giao dịch được khôi phục và nếu bạn không tạo lại giao dịch, việc thử lại của bạn sẽ thực hiện bên ngoài ngữ cảnh của giao dịch và có thể dẫn đến mâu thuẫn dữ liệu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.