2012-02-09 25 views
14

Tôi có một ứng dụng C# đang chèn dữ liệu vào bảng SQL Server (2008) bằng cách sử dụng thủ tục lưu sẵn. Tôi đang sử dụng đa luồng để làm điều này. Thủ tục lưu sẵn đang được gọi từ bên trong luồng. Bây giờ thủ tục được lưu trữ của tôi đang sử dụng "tablock" trong khi chèn dữ liệu. Trong khi thực hiện mã này, tôi nhận được lỗi sau: "Giao dịch (ID tiến trình) đã bị bế tắc trên tài nguyên khóa với một quy trình khác và đã được chọn làm nạn nhân bế tắc. Chạy lại giao dịch."Giao dịch (ID tiến trình) bị bế tắc trên tài nguyên khóa với một quy trình khác và đã được chọn làm nạn nhân bế tắc. Chạy lại giao dịch

Bất kỳ ai có thể vui lòng giúp tôi giải quyết vấn đề này không?

+2

Deadlock trên SQL Server, bạn sẽ phải thêm vào câu hỏi của bạn mã thủ tục lưu trữ cũng như các phần liên quan của giản đồ DB của bạn. – ken2k

Trả lời

12

Điều này xảy ra khi hai tiến trình Sql Server đang truy cập cùng một tài nguyên, nhưng theo thứ tự khác. Vì vậy, họ kết thúc cả hai chờ đợi cho quá trình khác, đó là một bế tắc.

Có một số cách để ngăn chặn nó, bao gồm:

  • Tránh dùng ổ khóa không cần thiết. Xem lại mức cách ly giao dịch được yêu cầu cho truy vấn, sử dụng gợi ý khóa with (nolock) cho các truy vấn nếu thích hợp.
  • Đảm bảo rằng khi lấy khóa, bạn lấy khóa trên các đối tượng theo cùng thứ tự trong mỗi truy vấn.

Ví dụ: nếu Proc1 khóa table1 và sau đó table2, nhưng Proc2 khóa table2 và sau đó table1, vấn đề có thể phát sinh. Bạn có thể viết lại hoặc proc để lấy khóa theo cùng thứ tự để tránh vấn đề này.

2

Bạn có thể đóng gói truy vấn của bạn trong một khối catch, và bắt số lỗi (liên quan đến ổ khóa)

  1. 1204
  2. 1205
  3. 1222

Sau đó, bạn có thể tự động thử lại , lên đến một số nhất định .. Vì vậy, bạn sẽ làm một cái gì đó như sau;

  DECLARE @RetryNo Int = 1 
    ,@RetryMaxNo Int = 5; 
    WHILE @RetryNo < @RetryMaxNo 
     BEGIN 
     BEGIN TRY 

     -- put your query that generates locks here.... 

      SELECT @RetryNo = @RetryMaxNo; 
     END TRY 
     BEGIN CATCH 
      IF ERROR_NUMBER() IN (1204, 1205, 1222) 
       BEGIN 
        SET @RetryNo += 1; 
        -- it will wait for 10 seconds to do another attempt 
        WAITFOR DELAY '00:00:10'; 
       END 
      ELSE 
       THROW; 
     END CATCH 
     END 

Bạn cũng có thể sử dụng các gợi ý bảng như UPDLOCK.

+0

Tại sao bạn sử dụng 'SELECT @RetryNo = @RetryMaxNo;' thay vì 'BREAK;'? – Storm

+0

Bởi vì tại thời điểm đó chúng tôi không muốn nó thất bại, chúng tôi muốn nhắc lại quá trình và cung cấp cho nó một số tiền tối đa của các nỗ lực, trước khi ném ngoại lệ (nếu nó liên quan đến thời gian chờ). – Mez

+0

Tại sao 'BREAK' làm cho nó thất bại? Nó sẽ không ném một ngoại lệ bởi vì nó đã được chuyển qua bất kỳ mã nào có thể làm cho nó thất bại, vì vậy 'BREAK' chỉ đơn giản là rút ngắn vòng lặp. – Storm

0

bạn có thể sử dụng từ Khóa đối tượng

 static object _lock = new object(); 
    public static void _main() 
    { 
      lock (_lock) 
      { 
       _bulkcopy(myData); 
      } 
    } 
    public static void _bulkcopy(DataTable dt) 
    { 
     try 
     { 
      using (var connection = new SqlConnection(ConfigurationSettings.AppSettings.Get("DBConnection"))) 
      { 
       connection.Open(); 
       SqlTransaction transaction = connection.BeginTransaction(); 

       using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) 
       { 
        bulkCopy.BatchSize = 100; 
        bulkCopy.DestinationTableName = "dbo.MyTable"; 
        try 
        { 
         bulkCopy.WriteToServer(dt); 
        } 
        catch (Exception) 
        { 
         transaction.Rollback(); 
         connection.Close(); 
        } 
       } 

       transaction.Commit(); 
      } 




     } 
     catch { } 
    } 
Các vấn đề liên quan