यहाँ समस्या पर मेरा विचार है:
-
SQL सर्वर, या किसी डेटाबेस में डेटा डालने/अपडेट/क्वेरी करने के लिए एकाधिक थ्रेड का उपयोग करते समय, गतिरोध जीवन का एक तथ्य है। आपको यह मानना होगा कि वे घटित होंगे और उन्हें उचित रूप से संभालना होगा।
-
ऐसा नहीं है कि हमें गतिरोध की घटना को सीमित करने का प्रयास नहीं करना चाहिए। हालाँकि, गतिरोध के मूल कारणों को पढ़ना और उन्हें रोकने के लिए कदम उठाना आसान है, लेकिन SQL सर्वर आपको हमेशा आश्चर्यचकित करेगा :-)
गतिरोध के कुछ कारण:
-
बहुत अधिक थ्रेड - थ्रेड्स की संख्या को न्यूनतम तक सीमित करने का प्रयास करें, लेकिन निश्चित रूप से हम अधिकतम प्रदर्शन के लिए अधिक थ्रेड्स चाहते हैं।
-
पर्याप्त सूचकांक नहीं। यदि चयन और अपडेट चयनात्मक नहीं हैं, तो SQL स्वस्थ की तुलना में बड़ी रेंज के ताले निकाल देगा। उपयुक्त अनुक्रमणिका निर्दिष्ट करने का प्रयास करें।
-
बहुत सारे इंडेक्स। अनुक्रमणिका को अद्यतन करने से गतिरोध उत्पन्न होता है, इसलिए अनुक्रमणिका को आवश्यक न्यूनतम तक कम करने का प्रयास करें।
-
लेन-देन अलगाव स्तर बहुत अधिक है। .NET का उपयोग करते समय डिफ़ॉल्ट आइसोलेशन स्तर 'Serializable' है, जबकि SQL सर्वर का उपयोग करने वाला डिफ़ॉल्ट 'रीड कमिटेड' है। अलगाव के स्तर को कम करने से बहुत मदद मिल सकती है (यदि उचित हो तो)।
मैं आपकी समस्या से इस तरह निपट सकता हूँ:
-
मैं अपना खुद का थ्रेडिंग समाधान नहीं रोल करूंगा, मैं टास्कपैरेलल लाइब्रेरी का उपयोग करूंगा। मेरी मुख्य विधि कुछ इस तरह दिखाई देगी:
using (var dc = new TestDataContext()) { // Get all the ids of interest. // I assume you mark successfully updated rows in some way // in the update transaction. List<int> ids = dc.TestItems.Where(...).Select(item => item.Id).ToList(); var problematicIds = new List<ErrorType>(); // Either allow the TaskParallel library to select what it considers // as the optimum degree of parallelism by omitting the // ParallelOptions parameter, or specify what you want. Parallel.ForEach(ids, new ParallelOptions {MaxDegreeOfParallelism = 8}, id => CalculateDetails(id, problematicIds)); }
-
गतिरोध विफलताओं के लिए पुनर्प्रयासों के साथ परिकलित विवरण विधि निष्पादित करें
private static void CalculateDetails(int id, List<ErrorType> problematicIds) { try { // Handle deadlocks DeadlockRetryHelper.Execute(() => CalculateDetails(id)); } catch (Exception e) { // Too many deadlock retries (or other exception). // Record so we can diagnose problem or retry later problematicIds.Add(new ErrorType(id, e)); } }
-
मूल गणना विवरण विधि
private static void CalculateDetails(int id) { // Creating a new DeviceContext is not expensive. // No need to create outside of this method. using (var dc = new TestDataContext()) { // TODO: adjust IsolationLevel to minimize deadlocks // If you don't need to change the isolation level // then you can remove the TransactionScope altogether using (var scope = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions {IsolationLevel = IsolationLevel.Serializable})) { TestItem item = dc.TestItems.Single(i => i.Id == id); // work done here dc.SubmitChanges(); scope.Complete(); } } }
-
और निश्चित रूप से मेरा एक गतिरोध पुनर्प्रयास सहायक का कार्यान्वयन
public static class DeadlockRetryHelper { private const int MaxRetries = 4; private const int SqlDeadlock = 1205; public static void Execute(Action action, int maxRetries = MaxRetries) { if (HasAmbientTransaction()) { // Deadlock blows out containing transaction // so no point retrying if already in tx. action(); } int retries = 0; while (retries < maxRetries) { try { action(); return; } catch (Exception e) { if (IsSqlDeadlock(e)) { retries++; // Delay subsequent retries - not sure if this helps or not Thread.Sleep(100 * retries); } else { throw; } } } action(); } private static bool HasAmbientTransaction() { return Transaction.Current != null; } private static bool IsSqlDeadlock(Exception exception) { if (exception == null) { return false; } var sqlException = exception as SqlException; if (sqlException != null && sqlException.Number == SqlDeadlock) { return true; } if (exception.InnerException != null) { return IsSqlDeadlock(exception.InnerException); } return false; } }
-
एक और संभावना एक विभाजन रणनीति का उपयोग करना है
यदि आपकी तालिकाओं को स्वाभाविक रूप से डेटा के कई अलग-अलग सेटों में विभाजित किया जा सकता है, तो आप या तो SQL सर्वर विभाजित तालिकाओं और अनुक्रमणिका का उपयोग कर सकते हैं, या आप अपनी मौजूदा तालिकाओं को तालिकाओं के कई सेटों में मैन्युअल रूप से विभाजित कर सकते हैं। मैं SQL सर्वर के विभाजन का उपयोग करने की सलाह दूंगा, क्योंकि दूसरा विकल्प गड़बड़ होगा। साथ ही अंतर्निर्मित विभाजन केवल SQL एंटरप्राइज़ संस्करण पर उपलब्ध है।
यदि आपके लिए विभाजन संभव है, तो आप एक ऐसी विभाजन योजना चुन सकते हैं जिसने आपको 8 अलग-अलग सेटों में डेटा तोड़ दिया हो। अब आप अपने मूल सिंगल थ्रेडेड कोड का उपयोग कर सकते हैं, लेकिन प्रत्येक के पास एक अलग विभाजन को लक्षित करने वाले 8 धागे हैं। अब कोई (या कम से कम न्यूनतम संख्या में) गतिरोध नहीं होगा।
मुझे उम्मीद है कि इसका कोई अर्थ है।