2009-06-06 13 views
7

पर डेडलॉक मुझे SQL Server 2008 पर SELECT/UPDATE पर डेडलॉक के साथ कोई समस्या हो रही है। मैंने इस धागे से उत्तर पढ़े हैं: SQL Server deadlocks between select/update or multiple selects लेकिन मुझे अभी भी समझ में नहीं आता कि मुझे डेडलॉक क्यों मिलता है।SELECT/UPDATE

मैंने निम्नलिखित टेस्टकेस में स्थिति को फिर से बनाया है।

मैं एक मेज है:

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 

मैं इस तालिका से एक रिकॉर्ड का चयन करने के पहले कोशिश कर रहा हूँ और रिकॉर्ड वर्तमान समय के साथ साथ कुछ अंतराल के लिए सेट समाप्ति समय मौजूद रहने पर। यह निम्नलिखित कोड का उपयोग कर पूरा किया है: अगर मैं दो धागे से परीक्षा पद्धति पर अमल करने की कोशिश

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(); 
     } 
    } 
} 

तो और वे एक ही रिकॉर्ड को अपडेट करने मैं उत्पादन निम्नलिखित पाने की कोशिश:

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

मैं कैसे नहीं समझ सकता यह Serializable अलगाव स्तर का उपयोग कर हो सकता है। मुझे लगता है कि पहले चयन करें पंक्ति/तालिका को लॉक करना चाहिए और किसी अन्य ताले को प्राप्त करने के लिए किसी अन्य को चुनने नहीं देगा। उदाहरण कमांड ऑब्जेक्ट्स का उपयोग करके लिखा गया है लेकिन यह केवल परीक्षण उद्देश्यों के लिए है। मूल रूप से, मैं linq का उपयोग कर रहा हूँ लेकिन मैं सरलीकृत उदाहरण दिखाना चाहता था। एसक्यूएल सर्वर प्रोफाइलर से पता चलता है कि डेडलॉक कुंजी लॉक है। मैं कुछ मिनटों में प्रश्न अपडेट करूँगा और एसक्यूएल सर्वर प्रोफाइलर से ग्राफ पोस्ट करूंगा। किसी भी सहायता की सराहना की जाएगी। मैं समझता हूं कि इस समस्या का समाधान कोड में महत्वपूर्ण खंड बना रहा है लेकिन मैं समझने की कोशिश कर रहा हूं कि सीरियलज़ेबल अलगाव स्तर चाल क्यों नहीं करता है। deadlock http://img7.imageshack.us/img7/9970/deadlock.gif

अग्रिम धन्यवाद:

और यहाँ गतिरोध ग्राफ है।

+0

+1! –

उत्तर

4

यह काम करने के लिए लॉकिंग पर संकेत देने के लिए आपको एक क्रमिक लेनदेन करने के लिए पर्याप्त नहीं है।

serializable अलगाव स्तर अभी भी आमतौर पर ताला यह कर सकते हैं जो यह सुनिश्चित करता है serializable शर्तें पूरी होने पर (दोहराने योग्य पढ़ता है, कोई प्रेत पंक्तियों आदि)

तो, आप एक साझा पर ताला हथियाने रहे हैं की "सबसे कमजोर" प्रकार का अधिग्रहण करेगा आपकी तालिका जो बाद में है (आपके क्रमिक लेनदेन में) an update lock. पर अपग्रेड करने का प्रयास कर रहा है अपग्रेड विफल हो जाएगा यदि कोई अन्य थ्रेड साझा लॉक धारण कर रहा है (यह काम करेगा यदि कोई अन्य कोई साझा लॉक नहीं रखता है)।

आप शायद निम्न के लिए इसे बदलना चाहते हैं: जब चयन किया जाता है

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

कि एक अद्यतन ताला सुनिश्चित करेगा प्राप्त किया जाता है (ताकि आप लॉक अपग्रेड करने की आवश्यकता नहीं होगा)। एक अच्छी तरह से प्रलेखित प्रश्न के लिए

+0

यह काम किया :) क्या linq2sql का उपयोग करके इसे हासिल करना संभव है? – empi

+0

कोई नहीं दिखता है ... http://stackoverflow.com/questions/806775/linq-to-sql-with-updlock –

+0

कल, मैंने कोशिश की (REPEATABLEREAD) क्योंकि मैंने पढ़ा है कि यह अद्यतन के लिए चयन के बराबर है लेकिन यह काम नहीं किया जैसा कि मैंने कहा कि अपडेटलॉक ने चाल बनाई थी। मुझे लगता है कि मैं linq2sql में updlock के बारे में नया सवाल खोलूंगा। उत्तर के लिए धन्यवाद, यह मुझे गंभीर सिरदर्द दे रहा था;) – empi