2009-06-06 26 views
7
üzerinde kilitlenme SQL Server 2008 SELECT/UPDATE üzerinde bir kilitlenme sorunu yaşıyorum. Bu iş parçacığının yanıtlarını okudum: SQL Server deadlocks between select/update or multiple selects Ama neden hala deadlock olsun anlamıyorum.

SELECT/UPDATE

Bu durumu aşağıdaki test kutusunda yeniden oluşturdum.

Ben bir tablo vardır:

CREATE TABLE [dbo].[SessionTest](
    [SessionId] UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL, 
    [ExpirationTime] DATETIME NOT NULL, 
    CONSTRAINT [PK_SessionTest] PRIMARY KEY CLUSTERED (
     [SessionId] ASC 
    ) WITH (
     PAD_INDEX = OFF, 
     STATISTICS_NORECOMPUTE = OFF, 
     IGNORE_DUP_KEY = OFF, 
     ALLOW_ROW_LOCKS = ON, 
     ALLOW_PAGE_LOCKS = ON 
    ) ON [PRIMARY] 
) ON [PRIMARY] 
GO 

ALTER TABLE [dbo].[SessionTest] 
    ADD CONSTRAINT [DF_SessionTest_SessionId] 
    DEFAULT (NEWID()) FOR [SessionId] 
GO 

Bu tablodan bir kayıt seçmek için ilk çalışıyorum ve kayıt şimdiki zaman artı bazı aralığına ayarlamak geçerlilik süresini varsa. Bu şu kod kullanılarak gerçekleştirilir: İki iş parçacığı gelen test yöntemini yürütmek çalışırsanız Sonra

protected Guid? GetSessionById(Guid sessionId, SqlConnection connection, SqlTransaction transaction) 
{ 
    Logger.LogInfo("Getting session by id"); 
    using (SqlCommand command = new SqlCommand()) 
    { 
     command.CommandText = "SELECT * FROM SessionTest WHERE SessionId = @SessionId"; 
     command.Connection = connection; 
     command.Transaction = transaction; 
     command.Parameters.Add(new SqlParameter("@SessionId", sessionId)); 

     using (SqlDataReader reader = command.ExecuteReader()) 
     { 
      if (reader.Read()) 
      { 
       Logger.LogInfo("Got it"); 
       return (Guid)reader["SessionId"]; 
      } 
      else 
      { 
       return null; 
      } 
     } 
    } 
} 

protected int UpdateSession(Guid sessionId, SqlConnection connection, SqlTransaction transaction) 
{ 
    Logger.LogInfo("Updating session"); 
    using (SqlCommand command = new SqlCommand()) 
    { 
     command.CommandText = "UPDATE SessionTest SET ExpirationTime = @ExpirationTime WHERE SessionId = @SessionId"; 
     command.Connection = connection; 
     command.Transaction = transaction; 
     command.Parameters.Add(new SqlParameter("@ExpirationTime", DateTime.Now.AddMinutes(20))); 
     command.Parameters.Add(new SqlParameter("@SessionId", sessionId)); 
     int result = command.ExecuteNonQuery(); 
     Logger.LogInfo("Updated"); 
     return result; 
    } 
} 

public void UpdateSessionTest(Guid sessionId) 
{ 
    using (SqlConnection connection = GetConnection()) 
    { 
     using (SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable)) 
     { 
      if (GetSessionById(sessionId, connection, transaction) != null) 
      { 
       Thread.Sleep(1000); 
       UpdateSession(sessionId, connection, transaction); 
      } 
      transaction.Commit(); 
     } 
    } 
} 

ve onlar çıktı şu olsun aynı kaydı güncellemeyi deneyin: Ben anlayamıyorum

[4] : Creating/updating session 
[3] : Creating/updating session 
[3] : Getting session by id 
[3] : Got it 
[4] : Getting session by id 
[4] : Got it 
[3] : Updating session 
[4] : Updating session 
[3] : Updated 
[4] : Exception: Transaction (Process ID 59) was deadlocked 
on lock resources with another process and has been 
chosen as the deadlock victim. Rerun the transaction. 

Seri hale getirilebilir yalıtım düzeyi kullanılarak gerçekleşebilir. İlk seçimde satır/tabloyu kilitlemeli ve başka bir kilitlenmeyi seçmemize izin vermemeliyim. Örnek komut nesneleri kullanılarak yazılmıştır, ancak sadece test amaçlıdır. Aslında linq kullanıyorum ama basitleştirilmiş örnek göstermek istedim. Sql Server Profiler kilitlenme kilit kilit olduğunu gösterir. Soruyu birkaç dakika içinde güncelleyeceğim ve sql server profilerden grafik yayınlayacağım. Herhangi bir yardım takdir edilecektir. Bu sorunun çözümünün kodda kritik bölüm oluşturduğunu anlıyorum, ancak Seri hale getirilebilir Yalıtım Seviyesinin neden hile yapmadığını anlamaya çalışıyorum. peşin deadlock http://img7.imageshack.us/img7/9970/deadlock.gif

Teşekkür: Burada

Ve

kilitlenme grafiktir.
+0

+1! –

cevap

4

Bu işlemin çalışması için kilitleme hakkında ipucu vermeniz gereken bir seri hale getirilebilir işlem olması yeterli değil.

seri hale getirilebilir yalıtım seviyesi hala genellikle seri hale getirilebilir koşulları (tekrarlanabilir okur, hayır hayalet satır vb) Böylece

, paylaşılan bir hedefe kilitlenmek kapma olan karşılanmasını sağlamaktadır elinden kilidin "en zayıf" türü satın alacak Daha sonra (seri hale getirilebilir işleminizde) olan tablonuz an update lock.'a yükseltilmeye çalışılıyor Başka bir iş parçacığı paylaşılan kilidi tutuyorsa yükseltme başarısız olur (paylaşılan bir kilit içermeyen başka bir gövde varsa çalışır).

Muhtemelen şu şekilde değiştirmek istiyorum: SEÇ gerçekleştirildiğinde bir güncelleme kilidi sağlayacaktır

SELECT * FROM SessionTest with (updlock) WHERE SessionId = @SessionId 

elde edilir (böylece kilidi yükseltmek gerekmez). İyi belgelenmiş bir soru için

+0

Çalıştı :) linq2sql kullanarak bunu başarmak mümkün mü? – empi

+0

Görünüşe göre yok ... http://stackoverflow.com/questions/806775/linq-to-sql-with-updlock –

+0

Dün, (REPEATABLEREAD) ile denedim çünkü bunun, UPDATE SEÇİCİ'ye eşdeğer olduğunu okudum ama işe yaramadı. Ben söyledim updlock hile yaptı. Ben linq2sql updlock hakkında yeni bir soru açacağını düşünüyorum. Cevabınız için teşekkürler, bana ciddi bir baş ağrısı veriyordu;) – empi