5

Chúng tôi gặp sự cố với một số mã cơ sở dữ liệu dường như thực thi với mức cách ly sai. Trong phần đặc biệt này của mã, nó được cho là thực thi với "READ UNCOMMITTED" để giảm thiểu khóa. Dữ liệu không nhất quán là OK vào thời điểm này."SET GIAO DIỆN CẤP ĐỌC ĐƯỢC ĐỌC" KHÔNG tham gia? Hay tôi đang đi sai đường?

Tuy nhiên, mã thực sự đọc với READ COMMITTED và chúng tôi không thể tìm ra lý do.

Đây là những gì chúng tôi đã làm:

  1. Mở kết nối
  2. Execute trên kết nối này "SET GIAO DỊCH ISOLATION LEVEL ĐỌC không bị giam"
  3. Hit một breakpoint
  4. Execute SQL

Trên điểm ngắt, chúng tôi đưa ra lệnh này tới cơ sở dữ liệu:

select s.session_id, s.transaction_isolation_level, st.text from sys.dm_exec_sessions s 
inner join sys.sysprocesses sp on (sp.spid = s.session_id) 
CROSS APPLY sys.dm_exec_sql_text(sp.sql_handle) st 

SQL này báo cáo 4 kết nối gộp lại ngay bây giờ, một trong số đó là kết nối của chúng ta mà chúng ta có thể bước xa hơn breakpoint để thực hiện SQL của chúng tôi với, có trạng thái này:

53 2 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 

tức. session 53 có mức cô lập 2 (READ COMMITTED) và câu lệnh SQL cuối cùng đã được thực hiện trong phiên này là lệnh "SET TRANSACTION ...".

Làm cách nào để thực hiện điều này?

Chúng tôi đã xác minh với SQL Profiler rằng kết nối này không tồn tại trước khi mã .NET của chúng tôi mở nó, vì vậy nó không được tái sử dụng từ nhóm kết nối.

Tuy nhiên, với một kết nối mới, và SQL đầu tiên và duy nhất được thực hiện trên nó rõ ràng đã nói với nó để sử dụng READ UNCOMMITTED, làm thế nào có thể kết nối vẫn còn được đọc COMMITTED?

Chúng ta nên xem xét điều gì ở đây?

Các chuỗi kết nối (với bit redacted) là như thế này:

SERVER=hostname;DATABASE=dbname;Integrated Security=false;USER ID=sa;PASSWORD=****;Application Name=appname;Type System Version=SQL Server 2000;Workstation ID=hostname; 

Các kết nối được bình thường SqlConnection kết nối, mở theo cách thông thường. Không may là chúng tôi không thể tái tạo vấn đề nếu chúng tôi viết mã bình thường mở một SqlConnection, vì vậy phải có một cái gì đó với trạng thái ứng dụng, nhưng vì SqlProfiler và Sql Server đều cho chúng ta biết rằng có, SQL đã được thực hiện, nhưng không, tôi không quan tâm.

Điều gì có thể ảnh hưởng đến điều này?

Mã chính xác cũng mở các kết nối khác, có nghĩa là mã được thực hiện nhiều lần và mở nhiều kết nối, vì vậy nhiều kết nối kết thúc trong hồ bơi, nhưng chỉ kết nối đầu tiên mới có vấn đề này.

Đây là SQL Server 2008 R2 và chúng tôi cũng đã sao chép vấn đề này vào năm 2012.

Sửa

OK, một số chi tiết thông tin.

Đầu tiên, chúng tôi đang cho phép tổng hợp, hoặc đúng hơn, chúng tôi không vô hiệu hóa nó một cách rõ ràng, cũng như chúng tôi không ghép nối chuỗi kết nối để tạo nhóm "N".

Tuy nhiên, kết nối này là lần đầu tiên được mở bằng chuỗi kết nối cụ thể này, do đó nó không được lấy ra khỏi hồ bơi. Ngoài ra, hãy xem ghi chú của tôi bên dưới về việc nó bị "vĩnh viễn" vĩnh viễn.

kết nối này đã được thiết lập như thế này:

var conn = new SqlConnection(...); 
conn.StateChance += connection_StateChange; 

private void connection_StateChange(Object sender, StateChangeEventArgs e) 
{ 
    if (e.CurrentState == ConnectionState.Open) 
    { 
     using (IDbCommand cmd = ((SqlConnection)sender).CreateCommand()) 
     { 
      cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; 
      cmd.ExecuteNonQuery(); 
     } 

Chúng tôi không thực hiện bất kỳ SQL khác trước đây.

Lưu ý rằng mã này được sử dụng nhiều lần lần trong suốt thời gian tồn tại của ứng dụng, nó chỉ là kết nối đầu tiên mà nó mở ra mà kết thúc là sai.

Kết nối này cũng sẽ bị bệnh vĩnh viễn. Vì mỗi lần chúng ta mở kết nối (mặc dù chúng ta có thể lấy nó ra khỏi nhóm kết nối), sự kiện thay đổi trạng thái trên thực thi, cố gắng thiết lập lại mức cô lập. Điều này cũng không thành công, nhưng chỉ cho kết nối duy nhất này.

Ngoài ra, chúng tôi đã tìm thấy một điều ảnh hưởng đến điều này vì tôi đã đăng câu hỏi này.

Bằng cách thay đổi chuỗi kết nối, mà tôi được đăng trên:

...;Type System Version=SQL Server 2000;... 

này:

...;Type System Version=SQL Server 2008;MultipleActiveResultSets=true;... 

thì vấn đề này sẽ biến mất, tại breakpoint được liệt kê ở trên, các kết nối hiện nay đã "ĐỌC UNCOMMITTED "trạng thái.

Đây là một cá trích đỏ, kết nối không còn được báo cáo trong tổng quan của chúng tôi cho đến khi chúng tôi thực sự đã thực thi mã ở đó.

Chúng tôi đang tiếp tục gỡ lỗi.

+0

Tôi đã sao chép mã của bạn ở trên và mở kết nối và tôi nhận được kết quả mong đợi của kết nối không được đọc. Phải có mã khác thiết lập mức cô lập của bạn –

+0

Có thể có điều gì đó đã đặt 'NOEXEC' thành' ON' trên kết nối này không? Nếu tôi làm điều đó trong một đợt và sau đó chạy 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED' trong loạt tiếp theo trong một cửa sổ truy vấn, tôi nhận được kết quả mà bạn đã hiển thị - lệnh được thực thi cuối cùng là' SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED' cột 'transaction_isolation_level' vẫn cho thấy nó được đặt thành 2. –

+0

Xem câu trả lời của riêng tôi ở đây, tôi không cảm thấy tự tin rằng tôi đã khám phá tất cả các đoạn mã cần thiết trong câu hỏi của mình, vì vậy tôi có hai ý muốn giữ nó ở đây, Tôi không cảm thấy đó là một câu hỏi hay để thành thật. –

Trả lời

4

Vấn đề ở đây là SqlConnection.BeginTransaction không tham số mặc định để đọc cam kết. Tôi đoán chúng tôi không hiểu văn bản "mức cô lập mặc định" ở trên trang đó là gì.

Đó trang có văn bản này:

Nếu bạn không chỉ định một mức độ cách ly, mức cô lập mặc định được sử dụng. Để chỉ định mức cách ly với phương thức BeginTransaction, sử dụng quá tải lấy tham số iso (BeginTransaction). Mức cô lập thiết lập cho một giao dịch vẫn tồn tại sau khi giao dịch được hoàn thành và cho đến khi kết nối được đóng hoặc xử lý. Đặt mức cô lập thành Ảnh chụp nhanh trong cơ sở dữ liệu nơi mức độ cách ly ảnh chụp nhanh không được bật không ném ngoại lệ. Giao dịch sẽ hoàn thành bằng cách sử dụng mức cô lập mặc định.

(điểm nhấn của tôi)

Dưới đây là một kịch bản LINQPad đó chứng tỏ:

void Main() 
{ 
    using (var conn = new SqlConnection("Data Source=.;Initial Catalog=master;Integrated security=true")) 
    { 
     conn.Open(); 
     Dump(conn, "after open"); 

     using (var cmd = new SqlCommand()) 
     { 
      cmd.Connection = conn; 
      cmd.CommandText = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"; 
      cmd.ExecuteNonQuery(); 
     } 

     Dump(conn, "after set iso"); 

     using (var cmd = new SqlCommand()) 
     { 
      cmd.Connection = conn; 
      cmd.CommandText = "BEGIN TRANSACTION"; 
      cmd.ExecuteNonQuery(); 
     } 

     Dump(conn, "after sql-based begin transaction"); 

     using (var cmd = new SqlCommand()) 
     { 
      cmd.Connection = conn; 
      cmd.CommandText = "COMMIT"; 
      cmd.ExecuteNonQuery(); 
     } 

     Dump(conn, "after sql-based commit"); 

     var trans = conn.BeginTransaction(); 

     Dump(conn, "after .net begin transaction", trans); 

     trans.Commit(); 

     Dump(conn, "after .net commit"); 
    } 
} 

public static void Dump(SqlConnection connection, string title, SqlTransaction transaction = null) 
{ 
    using (var cmd = new SqlCommand()) 
    { 
     cmd.Connection = connection; 
     if (transaction != null) 
      cmd.Transaction = transaction; 
     cmd.CommandText = "SELECT transaction_isolation_level FROM sys.dm_exec_sessions WHERE session_id = @@SPID"; 
     Debug.WriteLine(title + "=" + Convert.ToInt32(cmd.ExecuteScalar())); 
    } 
} 

Nó sẽ ra:

after open=2 
after set iso=1 
after sql-based begin transaction=1 
after sql-based commit=1 
after .net begin transaction=2 
after .net commit=2 

Ở đây bạn có thể thấy rằng bằng tay bắt đầu và cam kết một giao dịch thông qua SQL sẽ không thay đổi mức cô lập, nhưng bắt đầu một giao dịch trong .NET mà không nêu rõ mức cô lập vẫn thay đổi nó thành đọc cam kết. Kể từ khi mọi nơi chúng ta đọc, bắt đầu một giao dịch mà không nói rõ ràng mức cô lập nói rằng nó thừa hưởng mức cô lập của phiên, tôi đoán chúng ta không hiểu rằng .NET sẽ không làm như vậy.

+0

Bạn nên chấp nhận điều này làm câu trả lời đúng. – RBarryYoung

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