2010-02-02 8 views
6

मैं एक वितरित लेनदेन में एकाधिक डेटाबेस कनेक्शन के व्यवहार को निर्धारित करने की कोशिश कर रहा हूं।वितरित लेनदेन एक थ्रेड किए गए वातावरण में एक ही डीबी के एकाधिक कनेक्शन के साथ कैसे व्यवहार करते हैं?

मुझे एक लंबी चल रही प्रक्रिया मिली है जो धागे की एक श्रृंखला को जन्म देती है और प्रत्येक थ्रेड उसके 'डीबी कनेक्शन और इस तरह के प्रबंधन के लिए जिम्मेदार होता है। यह सब लेनदेन के दायरे के अंदर चलाता है और प्रत्येक थ्रेड को DependentTransaction ऑब्जेक्ट के माध्यम से लेनदेन में सूचीबद्ध किया जाता है।

जब मैं इस प्रक्रिया को समानांतर में रखने के लिए गया तो मैं कुछ मुद्दों में भाग गया, अर्थात् ऐसा लगता है कि कुछ प्रकार के ब्लॉक लेनदेन पर एक ही समय में निष्पादन से रोकने से रोकते हैं।

मैं क्या जानना चाहता हूं कि लेनदेन समन्वयक एक ही डीबी से एकाधिक कनेक्शन से प्रश्नों को कैसे संभालता है और यदि थ्रेड में कनेक्शन ऑब्जेक्ट को पास करने की भी सलाह दी जाती है?

मैंने पढ़ा है कि एमएस एसक्यूएल केवल प्रति लेनदेन एक कनेक्शन की अनुमति देता है लेकिन मैं उसी लेनदेन में एक ही डीबी के एक से अधिक कनेक्शन बनाने और आरंभ करने में स्पष्ट रूप से सक्षम हूं। मैं कनेक्शन खोलते समय "किसी अन्य सत्र द्वारा उपयोग में लेनदेन संदर्भ" अपवाद प्राप्त किए बिना समानांतर में धागे को निष्पादित करने में सक्षम नहीं हूं। नतीजा यह है कि कनेक्शन को एक ही समय में चलाने के बजाए निष्पादित करने के लिए इंतजार करना पड़ता है और अंत में कोड पूरा होने तक चलता है लेकिन इस लॉकिंग मुद्दे के कारण ऐप को थ्रेड करने के लिए कोई शुद्ध लाभ नहीं होता है।

कोड इस तरह कुछ दिखता है।

Sub StartThreads() 
     Using Scope As New TransactionScope 
      Dim TL(100) As Tasks.Task 
      Dim dTx As DependentTransaction 
      For i As Int32 = 0 To 100 
       Dim A(1) As Object 
       dTx = CType(Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete), DependentTransaction) 
       'A(0) = some_other_data 
       A(1) = dTx 'the Dependent Transaction 

       TL(i) = Tasks.Task.Factory.StartNew(AddressOf Me.ProcessData, A) 'Start the thread and add it to the array 
      Next 

      Tasks.Task.WaitAll(TL) 'Wait for threads to finish 

      Scope.Complete() 
     End Using 
    End Sub 
    Dim TransLock As New Object 
    Sub ProcessData(ByVal A As Object) 
     Dim DTX As DependentTransaction = A(1) 
     Dim Trans As Transactions.TransactionScope 
     Dim I As Int32 
     Do While True 
      Try 
       SyncLock (TransLock) 
        Trans = New Transactions.TransactionScope(DTX, TimeSpan.FromMinutes(1)) 
       End SyncLock 
       Exit Do 
      Catch ex As TransactionAbortedException 
       If ex.ToString.Contains("Failure while attempting to promote transaction") Then 
       ElseIf ex.Message = "The transaction has aborted." Then 
        Throw New Exception(ex.ToString) 
        Exit Sub 
       End If 
       I += 1 
       If I > 5 Then 
        Throw New Exception(ex.ToString) 
       End If 
      Catch ex As Exception 

      End Try 
      Thread.Sleep(10) 
     Loop 
     Using Trans 
      Using DALS As New DAC.DALScope 
       Do While True 
        Try 
         SyncLock (TransLock) 
          'This opens two connection to the same DB for later use. 
          DALS.CurrentDAL.OpenConnection(DAC.DAL.ConnectionList.FirstConnection) 
          DALS.CurrentDAL.OpenConnection(DAC.DAL.ConnectionList.SecondConnection) 
         End SyncLock 
         Exit Do 
        Catch ex As Exception 
         'This is usually where I find the bottleneck 
         '"Transaction context in use by another session" is the exception that I get 
         Thread.Sleep(100) 
        End Try 
       Loop 

       '***************** 
       'Do some work here 
       '***************** 

       Trans.Complete() 
      End Using 
     End Using 
     DTX.Complete() 
    End Sub 

संपादित

मेरे परीक्षण अंतिम तौर से पता चला है कि यह सिर्फ नहीं किया जा सकता है। यहां तक ​​कि अगर एक से अधिक कनेक्शन हैं या लेनदेन में सभी अनुरोधों का उपयोग किया जाता है या प्रश्न क्रमशः संसाधित होते हैं।

शायद वे भविष्य में इस व्यवहार को बदल देंगे।

+0

ठीक मैं देख रहा हूँ आप एक दुविधा है। लेकिन एसक्यूएल के परिप्रेक्ष्य से एक लेनदेन की सीमाएं होनी चाहिए। मेरे दिमाग में कई कनेक्शनों में लेनदेन का सम्मान करने के लिए अवधारणा का उल्लंघन होता है। मैं अपने खाने के साथ अपने सलाद के लिए पूछ सकता हूं लेकिन मैं नहीं चाहता कि अगली टेबल कहें कि वे मेरा सलाद चाहते हैं। – Paparazzi

उत्तर

8

सबसे पहले, आपको एसक्यूएल सर्वर लेनदेन के बारे में यहां और वहां 2 अलग-अलग मामलों में पढ़ना होगा: स्थानीय और वितरित।

स्थानीय एसक्यूएल लेनदेन:

  • एसक्यूएल सर्वर एक ही अनुरोध प्रत्येक स्थानीय लेन-देन पर अमल करने के लिए अनुमति देता है।
  • डिफ़ॉल्ट रूप से केवल एक सत्र स्थानीय लेनदेन में नामांकन कर सकता है। Sp_getbindtoken और sp_bindsession एकाधिक सत्रों का उपयोग स्थानीय लेनदेन में नामांकित किया जा सकता है। सत्र अभी भी किसी भी समय अनुरोध को निष्पादित करने के लिए प्रतिबंधित हैं।
  • एकाधिक सक्रिय परिणाम समूह (एमएआरएस) के साथ एक सत्र एकाधिक अनुरोध निष्पादित कर सकता है। सभी अनुरोधों को उसी स्थानीय लेनदेन में नामांकित होना होगा।

वितरित लेन-देन:

  • एकाधिक सत्र उनके स्थानीय लेन-देन एक भी वितरित लेन-देन में दाखिला लिया हो सकता है।
  • प्रत्येक सत्र अभी भी एक स्थानीय लेनदेन में enroled है, स्थानीय लेनदेन
  • स्थानीय एक वितरित लेनदेन में enroled लेनदेन दो चरण के अधीन
  • सभी स्थानीय लेनदेन पर वितरित लेनदेन द्वारा समन्वित प्रतिबद्ध हैं के लिए ऊपर बताए सभी प्रतिबंधों के अधीन एक वितरित लेनदेन में नामांकित एक उदाहरण अभी भी स्वतंत्र स्थानीय लेनदेन है, जिसका मुख्य अर्थ है कि उनके पास विवादित ताला नामस्थान हैं।

तो जब कोई ग्राहक .NET लेनदेन स्कोप बनाता है और इस लेनदेन के दायरे में यह उसी सर्वर पर एकाधिक अनुरोध निष्पादित करता है, तो ये अनुरोध सभी स्थानीय लेनदेन वितरित लेनदेन में नामांकित होते हैं। एक साधारण उदाहरण:

class Program 
    { 
     static string sqlBatch = @" 
set nocount on; 
declare @i int; 
set @i = 0; 
while @i < 100000 
begin 
    insert into test (a) values (replicate('a',100)); 
    set @i = @i+1; 
end"; 

     static void Main(string[] args) 
     { 
      try 
      { 
       TransactionOptions to = new TransactionOptions(); 
       to.IsolationLevel = IsolationLevel.ReadCommitted; 
       using (TransactionScope scp = new TransactionScope(TransactionScopeOption.Required, to)) 
       { 
        using (SqlConnection connA = new SqlConnection(Settings.Default.connString)) 
        { 
         connA.Open(); 
         using (SqlConnection connB = new SqlConnection(Settings.Default.connString)) 
         { 
          connB.Open(); 

          SqlCommand cmdA = new SqlCommand(sqlBatch, connA); 
          SqlCommand cmdB = new SqlCommand(sqlBatch, connB); 

          IAsyncResult arA = cmdA.BeginExecuteNonQuery(); 
          IAsyncResult arB = cmdB.BeginExecuteNonQuery(); 

          WaitHandle.WaitAll(new WaitHandle[] { arA.AsyncWaitHandle, arB.AsyncWaitHandle }); 

          cmdA.EndExecuteNonQuery(arA); 
          cmdB.EndExecuteNonQuery(arB); 
         } 
        } 
        scp.Complete(); 
       } 
      } 
      catch (Exception e) 
      { 
       Console.Error.Write(e); 
      } 
     } 
    } 

एक डमी परीक्षण तालिका बनाएँ:

create table test (id int not null identity(1,1) primary key, a varchar(100)); 

और मेरे नमूने में कोड चलाते हैं। आप देखेंगे कि दोनों अनुरोध समानांतर में निष्पादित कर रहे हैं, प्रत्येक तालिका में 100k पंक्तियों को सम्मिलित कर रहा है, फिर लेनदेन का दायरा पूरा होने पर दोनों प्रतिबद्ध होते हैं। इसलिए जो समस्याएं आप देख रहे हैं वे SQL सर्वर से संबंधित नहीं हैं और न ही लेनदेनस्कोप से संबंधित हैं, वे आपके द्वारा वर्णित परिदृश्य को आसानी से संभाल सकते हैं। अधिक, कोड बहुत सरल और सीधा आगे है और निर्भर लेनदेन के लिए कोई ज़रूरत नहीं है, होने वाली क्लोनिंग और न ही लेनदेन को बढ़ावा दिया जाना चाहिए।

अपडेट किया गया

स्पष्ट धागे और आश्रित लेनदेन का उपयोग करना:

private class ThreadState 
    { 
     public DependentTransaction Transaction {get; set;} 
     public EventWaitHandle Done {get; set;} 
     public SqlConnection Connection { get; set; } 
    } 
    static void Main(string[] args) 
    { 
     try 
     { 
      TransactionOptions to = new TransactionOptions(); 
      to.IsolationLevel = IsolationLevel.ReadCommitted; 
      using (TransactionScope scp = new TransactionScope(TransactionScopeOption.Required, to)) 
      { 
       ThreadState stateA = new ThreadState 
       { 
        Transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete), 
        Done = new AutoResetEvent(false), 
        Connection = new SqlConnection(Settings.Default.connString), 
       }; 
       stateA.Connection.Open(); 
       ThreadState stateB = new ThreadState 
       { 
        Transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete), 
        Done = new AutoResetEvent(false), 
        Connection = new SqlConnection(Settings.Default.connString), 
       }; 
       stateB.Connection.Open(); 

       ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), stateA); 
       ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), stateB); 

       WaitHandle.WaitAll(new WaitHandle[] { stateA.Done, stateB.Done }); 

       scp.Complete(); 

       //TODO: dispose the open connections 
      } 

     } 
     catch (Exception e) 
     { 
      Console.Error.Write(e); 
     } 
    } 

    private static void Worker(object args) 
    { 
     Debug.Assert(args is ThreadState); 
     ThreadState state = (ThreadState) args; 
     try 
     { 
      using (TransactionScope scp = new TransactionScope(state.Transaction)) 
      { 
       SqlCommand cmd = new SqlCommand(sqlBatch, state.Connection); 
       cmd.ExecuteNonQuery(); 
       scp.Complete(); 
      } 
      state.Transaction.Complete(); 
     } 
     catch (Exception e) 
     { 
      Console.Error.WriteLine(e); 
      state.Transaction.Rollback(); 
     } 
     finally 
     { 
      state.Done.Set(); 
     } 

    } 
+0

प्रतिक्रिया के लिए धन्यवाद। आप इसे एकाधिक धागे के साथ कैसे करेंगे? मुझे अपने परिदृश्य में कई धागे चलाने में सक्षम होना चाहिए और इसलिए लेनदेन उनके चारों ओर वितरित किया जाएगा। आपकी पोस्ट के आधार पर ऐसा लगता है कि मुझे थ्रेड में कनेक्शन ऑब्जेक्ट पास करना होगा। क्या यह तब सही मूल्यांकन है? – Middletone

+0

मेरा अपडेट देखें। मैं थ्रेड में समापन खोलने के लिए आश्रित ट्रांसकेशन प्राप्त करने में सक्षम नहीं था, मुझे पहले से ही खुले कनेक्शन को पास करना पड़ा था (और मुझे लगता है कि पहले ही डीटीसी में सूचीबद्ध है)। –

+0

मेरे परीक्षणों ने निष्कर्ष निकाला है कि यह अभी नहीं किया जा सकता है। यहां तक ​​कि अगर एक से अधिक कनेक्शन हैं या लेनदेन में सभी अनुरोधों का उपयोग किया जाता है या प्रश्न क्रमशः संसाधित होते हैं। शायद वे भविष्य में इस व्यवहार को बदल देंगे। – Middletone

संबंधित मुद्दे

 संबंधित मुद्दे