2010-11-10 57 views
5

Nhật ký sự kiện cho ứng dụng .NET của tôi cho thấy thỉnh thoảng nó bị khóa khi đọc từ Sql Server. Điều này thường rất hiếm khi chúng tôi đã tối ưu hóa các truy vấn của chúng tôi để tránh deadlocks, nhưng đôi khi chúng vẫn xảy ra. Trong quá khứ, chúng tôi đã có một số deadlocks xảy ra trong khi gọi hàm ExecuteReader trên phiên bản SqlCommand của chúng tôi. Để sửa mã retry chúng tôi đã thêm này chỉ đơn giản là chạy các truy vấn một lần nữa, như thế này:Cách khôi phục từ ngoại lệ bế tắc trong SqlDataReader.Đọc()

//try to run the query five times if a deadlock happends 
int DeadLockRetry = 5; 

while (DeadLockRetry > 0) 
{ 
    try 
    { 
     return dbCommand.ExecuteReader(); 
    } 
    catch (SqlException err) 
    { 
     //throw an exception if the error is not a deadlock or the deadlock still exists after 5 tries 
     if (err.Number != 1205 || --DeadLockRetry == 0) 
      throw; 
    } 
} 

này đã làm việc rất tốt cho các trường hợp bế tắc xảy ra trong truy vấn ban đầu thực hiện, nhưng bây giờ chúng tôi đang nhận được sự bế tắc trong khi lặp qua các kết quả bằng cách sử dụng hàm Read() trên trả lại SqlDataReader.

Một lần nữa, tôi không quan tâm đến việc tối ưu hóa truy vấn, nhưng chỉ cố gắng phục hồi trong trường hợp hiếm hoi là bế tắc xảy ra. Tôi đã suy nghĩ về việc sử dụng một quá trình thử lại tương tự. Tôi có thể tạo lớp của riêng mình kế thừa từ SqlDataReader chỉ đơn giản ghi đè hàm Read với thử lại mã. Như thế này:

public class MyDataReader : SqlDataReader 
{ 
    public override bool Read() 
    { 
     int DeadLockRetry = 5; 
     while (DeadLockRetry > 0) 
     { 
      try 
      { 
       return base.Read(); 
      } 
      catch (SqlException ex) 
      { 
       if (ex.ErrorCode != 1205 || --DeadLockRetry == 0) 
        throw; 
      } 
     } 
     return false; 
    } 
} 

Đây có phải là cách tiếp cận phù hợp không? Tôi muốn chắc chắn rằng các hồ sơ không bị bỏ qua trong đầu đọc. Sẽ thử lại Read sau khi bế tắc bỏ qua bất kỳ hàng nào? Ngoài ra, tôi nên gọi Thread.Sleep giữa các lần thử lại để cung cấp cho cơ sở dữ liệu thời gian thoát khỏi trạng thái bế tắc, hoặc là đủ. Trường hợp này không dễ tái tạo nên tôi muốn chắc chắn về nó trước khi tôi sửa đổi bất kỳ mã nào của mình.

EDIT:

Theo yêu cầu, một số thông tin thêm về tình hình của tôi: Trong một trường hợp, tôi có một quá trình mà thực hiện một truy vấn mà tải một danh sách các id kỷ lục mà cần phải được cập nhật. Sau đó, tôi lặp qua danh sách các id bằng cách sử dụng hàm Read và chạy một quá trình cập nhật trên bản ghi đó, cuối cùng sẽ cập nhật giá trị cho bản ghi đó trong cơ sở dữ liệu. (Không, không có cách nào để thực hiện cập nhật trong truy vấn ban đầu, nhiều thứ khác xảy ra cho mỗi bản ghi được trả lại). Mã này đã hoạt động tốt trong một thời gian, nhưng chúng tôi đang chạy khá nhiều mã cho mỗi bản ghi, vì vậy tôi có thể tưởng tượng một trong những quy trình đó đang tạo khóa trên bảng ban đầu đang được đọc qua.

Sau một vài suy nghĩ, đề xuất của Scottie để sử dụng cấu trúc dữ liệu để lưu trữ kết quả có thể sẽ khắc phục tình trạng này. Tôi có thể lưu trữ các id được trả lại trong một List<int> và lặp lại thông qua đó. Bằng cách đó, các khóa trên các hàng có thể được loại bỏ ngay lập tức.

Tuy nhiên, tôi vẫn muốn biết nếu có cách chung để khôi phục từ deadlocks trên một đọc.

+0

Ông có thể cho biết thêm về việc sử dụng DB của bạn? Thật kỳ lạ khi bị bế tắc trong khi đọc dữ liệu. Điều đó có nghĩa là mã của bạn đã giữ một số khóa cho các dữ liệu khác và một giao dịch khác đã khóa những gì bạn đang cố gắng đọc và đang đợi dữ liệu mà bạn đã truy cập trước đó. Bạn có thể gọi ExecuteReader trên một giao dịch khác không? – Timores

Trả lời

4

Toàn bộ giao dịch của bạn bị mất trong bế tắc. Bạn phải khởi động lại từ đầu, từ cách vượt quá mức bạn đọc trình đọc dữ liệu. Bạn nói rằng bạn đọc một số hồ sơ, sau đó cập nhật chúng trong một vòng lặp.Bạn phải khởi động lại với đọc lại các hồ sơ:

function DoWork() { 
    using (TransactionScope scope = new TransactionScope(...)) { 
    cmd = new SqlCommand("select ..."); 
    using (DataReader rdr = cmd.ExecuteReader()) { 
     while(rdr.Read()) { 
      ... process each record 
     } 
    } 
    scope.Complete(); 
    } 
} 

bạn phải thử lại các cuộc gọi toàn bộDoWork:

retries = 0; 
success = false; 
do { 
try { 
    DoWork(); 
    success = true; 
} 
catch (SqlException e) { 
    if (can retry e) { 
    ++retries; 
    } 
    else { 
    throw; 
    } 
} 
} while (!success); 
+0

Điều này. Bạn không thể thử đọc lại, bạn cần chạy lại lệnh. Nhưng bạn chắc chắn nên giới hạn số lần thử lại đến 3-5 tối đa, phải mất một thời gian để máy chủ SQL xử lý deadlocks. –

+0

Ok cảm ơn, đây là những gì tôi đang tìm kiếm. Chúng ta có một lớp dữ liệu trừu tượng trả về IDataReaders cho tất cả các truy vấn đọc của chúng ta, và sau đó mã logic của chúng ta sử dụng các độc giả. Vì vậy, trong ứng dụng của tôi mã hóa đọc của tôi như bạn đề xuất là không dễ dàng. Tôi đã hy vọng cho một giải pháp chung, nhưng có vẻ như điều này là không thể. Tôi sẽ xem xét việc sử dụng gợi ý này cho các trường hợp cụ thể gây ra các sự tắc nghẽn không thường xuyên. Cảm ơn tất cả các bạn gợi ý! – InvisibleBacon

0

Bạn có thể xem xét sử dụng DataSets hoặc DataTables thay vì DataReaders không?

Nỗi sợ hãi của tôi ở đây, mặc dù tôi không tích cực, là đọc đọc lỗi sẽ loại bỏ hoàn toàn bản ghi đó. Bạn có thể muốn kiểm tra điều này trong một môi trường được kiểm soát, nơi bạn có thể ép buộc lỗi và xem liệu nó có đọc lại bản ghi hay không nếu nó chỉ loại bỏ nó.

+0

Tôi khá chắc chắn rằng bằng cách sử dụng một DataTable hoặc thiết lập sẽ có những lỗ hổng tương tự. NET sẽ không sử dụng Đọc nội bộ để điền vào DataTable? Tôi chắc chắn muốn có thể kiểm tra điều này trong một môi trường được kiểm soát. Không chắc chắn chính xác nguyên nhân gây ra deadlocks, vì vậy điều này sẽ rất khó khăn. – InvisibleBacon

+0

Bạn không cần phải kiểm tra bế tắc cụ thể. Nếu bạn đang ở trong một môi trường được kiểm soát, bạn có thể lấy máy chủ SQL xuống trong một giây và bẫy lỗi đó. Điều đó ít nhất sẽ cho bạn biết nếu hồ sơ được đọc lại hoặc bị loại bỏ. – Scottie

+0

Có, DataSets sử dụng cùng một cơ chế đọc, nhưng tôi tự hỏi nếu MS có mã bế tắc đã có sẵn khi đọc? – Scottie

Các vấn đề liên quan