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