क्या लूप करते समय इसमें स्पष्ट लेनदेन की आवश्यकता होती है?


11

SQL सर्वर 2014:

हमारे पास एक बहुत बड़ी (100million पंक्ति) तालिका है, और हमें उस पर कुछ फ़ील्ड अपडेट करने की आवश्यकता है।

लॉग शिपिंग, आदि के लिए, हम भी, जाहिर है, इसे काटने के आकार के लेनदेन के लिए रखना चाहते हैं।

यदि हम नीचे को थोड़ा सा चलाते हैं, और फिर क्वेरी को रद्द / समाप्त कर देते हैं, तो क्या अब तक किए गए कार्य सभी प्रतिबद्ध होंगे, या क्या हमें स्पष्ट BEGIN TRANSACTION / END ट्रांजेक्शन स्टेटमेंट जोड़ने की आवश्यकता है ताकि हम किसी भी समय रद्द कर सकें?

DECLARE @CHUNK_SIZE int
SET @CHUNK_SIZE = 10000

UPDATE TOP(@CHUNK_SIZE) [huge-table] set deleted = 0, deletedDate = '2000-01-01'
where deleted is null or deletedDate is null

WHILE @@ROWCOUNT > 0
BEGIN
    UPDATE TOP(@CHUNK_SIZE) [huge-table] set deleted = 0, deletedDate = '2000-01-01'
    where deleted is null or deletedDate is null
END

जवाबों:


13

व्यक्तिगत बयान - डीएमएल, डीडीएल, आदि - स्वयं में लेनदेन हैं। तो हाँ, लूप के प्रत्येक पुनरावृत्ति के बाद (तकनीकी रूप से: प्रत्येक कथन के बाद), जो भी उस UPDATEकथन को बदल दिया गया है वह स्वतः-प्रतिबद्ध है।

बेशक, हमेशा एक अपवाद होता है, है ना? SET IMPLICIT_TRANSACTIONS के माध्यम से इम्प्लिक्ट ट्रांजैक्शंस को इनेबल करना संभव है , इस स्थिति में पहला UPDATEस्टेटमेंट एक ट्रांजैक्शन शुरू करेगा जो आपको COMMITया ROLLBACKअंत में करना होगा। यह एक सत्र स्तर सेटिंग है जो अधिकतर मामलों में डिफ़ॉल्ट रूप से बंद होती है।

क्या हमें स्पष्ट BEGIN TRANSACTION / END TRANSACTION कथनों को जोड़ने की आवश्यकता है ताकि हम किसी भी समय रद्द कर सकें?

नहीं। और वास्तव में, यह देखते हुए कि आप प्रक्रिया को रोकना चाहते हैं और पुनः आरंभ करना चाहते हैं, एक स्पष्ट लेनदेन (या इम्प्लांट ट्रांजैक्शंस को सक्षम करना) जोड़ना एक बुरा विचार होगा क्योंकि प्रक्रिया को रोकने से पहले इसे पकड़ सकते हैं COMMIT। उस स्थिति में आपको COMMIT(यदि आप SSMS में हैं) मैन्युअल रूप से जारी करने की आवश्यकता होगी , या यदि आप SQL एजेंट नौकरी से इसे चला रहे हैं, तो आपके पास वह अवसर नहीं है और एक अनाथ लेनदेन के साथ समाप्त हो सकता है।


इसके अलावा, आप @CHUNK_SIZEकम संख्या में सेट करना चाह सकते हैं । लॉक एस्केलेशन आम तौर पर एक ही वस्तु पर प्राप्त 5000 तालों पर होता है। पंक्तियों के आकार पर निर्भर करता है और यदि यह रो लॉक्स बनाम पेज लॉक कर रहा है, तो आप उस सीमा पर जा सकते हैं। यदि एक पंक्ति का आकार ऐसा है कि प्रत्येक पृष्ठ पर केवल 1 या 2 पंक्तियाँ फिट हैं, तो आप हमेशा इसे मार सकते हैं, भले ही यह पृष्ठ लॉक कर रहा हो।

यदि तालिका का विभाजन हुआ है, तो आपके पास तालिका के लिए LOCK_ESCALATIONविकल्प (SQL Server 2008 में पेश किया गया) सेट करने का विकल्प है AUTOताकि यह केवल विभाजन को लॉक करे और आगे बढ़ने पर पूरी तालिका को नहीं। या, किसी भी तालिका के लिए आप उसी विकल्प को निर्धारित कर सकते हैं DISABLE, हालांकि आपको इसके बारे में बहुत सावधान रहना होगा। देखें ALTER तालिका जानकारी के लिए।

यहां कुछ दस्तावेज़ हैं जो लॉक एस्केलेशन और थ्रेसहोल्ड के बारे में बात करते हैं: लॉक एस्केलेशन (यह कहता है "SQL सर्वर 2008 R2 और उच्चतर संस्करणों पर लागू होता है")। और यहां एक ब्लॉग पोस्ट है जो लॉक एस्केलेशन का पता लगाने और ठीक करने से संबंधित है: Microsoft SQL सर्वर में लॉकिंग (भाग 12 - लॉक एस्केलेशन)


सटीक प्रश्न से संबंधित, लेकिन प्रश्न में क्वेरी से संबंधित, कुछ सुधार हैं जो यहां किए जा सकते हैं (या कम से कम यह सिर्फ इसे देखने से ऐसा लगता है):

  1. अपने लूप के लिए, करना WHILE (@@ROWCOUNT = @CHUNK_SIZE)थोड़ा बेहतर है क्योंकि यदि अंतिम चलना पर अपडेट की गई पंक्तियों की संख्या UPDATE को अनुरोध की गई राशि से कम है, तो ऐसा करने के लिए कोई काम नहीं बचा है।

  2. यदि deletedक्षेत्र एक है BITडेटाप्रकार, तो वह मूल्य निर्धारित किया जाए या नहीं द्वारा नहीं है deletedDateहै 2000-01-01? आपको दोनों की आवश्यकता क्यों है?

  3. यदि ये दोनों क्षेत्र नए हैं और आपने इन्हें जोड़ा है NULLतो यह एक ऑनलाइन / नॉन-ब्लॉकिंग ऑपरेशन हो सकता है और अब इन्हें अपने "डिफ़ॉल्ट" मान में अपडेट करना चाहते हैं, तो यह आवश्यक नहीं था। SQL सर्वर 2012 (केवल एंटरप्राइज़ संस्करण) में प्रारंभ करना, उन NOT NULLस्तंभों को जोड़ना जो एक DEFAULT बाधा हैं, तब तक गैर-अवरुद्ध संचालन हैं जब तक DEFAULT का मान एक स्थिर होता है। इसलिए यदि आप अभी तक खेतों का उपयोग नहीं कर रहे हैं, तो केवल एक ड्रॉप- NOT NULLडे बाधा के रूप में और छोड़ दें ।

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

    1. एक अस्थायी तालिका (#FullSet) बनाएं, जिसमें बस इसमें मौजूद क्लस्टर इंडेक्स से प्रमुख फ़ील्ड हों।
    2. उसी संरचना की दूसरी अस्थायी तालिका (#CurrentSet) बनाएं।
    3. #FullSet के माध्यम से डालें SELECT TOP(n) KeyField1, KeyField2 FROM [huge-table] where deleted is null or deletedDate is null;

      TOP(n)टेबल के आकार के कारण वहाँ में है। तालिका में 100 मिलियन पंक्तियों के साथ, आपको वास्तव में कुंजियों के उस पूरे सेट के साथ तालिका को आबाद करने की आवश्यकता नहीं है, खासकर यदि आप इस प्रक्रिया को इतनी बार रोकने और बाद में इसे फिर से शुरू करने की योजना बनाते हैं। तो शायद n1 मिलियन पर सेट करें और उसे पूरा होने दें। आप इसे हमेशा SQL एजेंट नौकरी में शेड्यूल कर सकते हैं जो 1 मिलियन (या शायद इससे भी कम) का सेट चलाता है और फिर से लेने के लिए अगले निर्धारित समय की प्रतीक्षा करता है। फिर आप हर 20 मिनट में चलने का समय निर्धारित कर सकते हैं, ताकि सेट के बीच कुछ लागू किया गया सांस लेने का कमरा nहोगा, लेकिन यह अभी भी पूरी प्रक्रिया को समाप्त कर देगा। फिर बस काम को खुद ही हटा देना है जब कुछ करना नहीं है :-)।

    4. एक पाश में, करो:
      1. वर्तमान बैच को कुछ के माध्यम से पॉप्युलेट करें DELETE TOP (4995) FROM #FullSet OUTPUT Deleted.KeyField INTO #CurrentSet (KeyField);
      2. IF (@@ROWCOUNT = 0) BREAK;
      3. कुछ का उपयोग कर अद्यतन करें: UPDATE ht SET ht.deleted = 0, ht.deletedDate='2000-01-01' FROM [huge-table] ht INNER JOIN #CurrentSet cs ON cs.KeyField = ht.KeyField;
      4. वर्तमान सेट साफ़ करें: TRUNCATE TABLE #CurrentSet;
  5. कुछ मामलों में यह टेम्परिंग इंडेक्स को जोड़ने में मदद करता है ताकि टेम्प टेबल SELECTमें फीड हो सके #FullSet। इस तरह के सूचकांक को जोड़ने से संबंधित कुछ विचार इस प्रकार हैं:
    1. WHERE की स्थिति को आपकी क्वेरी की WHERE की स्थिति से मेल खाना चाहिए, इसलिए WHERE deleted is null or deletedDate is null
    2. प्रक्रिया की शुरुआत में, अधिकांश पंक्तियाँ आपके WHERE की स्थिति से मेल खाएँगी, इसलिए कोई अनुक्रमणिका सहायक नहीं है। इसे जोड़ने से पहले आप 50% के आसपास कहीं इंतजार करना चाहते हैं। बेशक, यह कितना मदद करता है और जब सूचकांक को जोड़ना सबसे अच्छा होता है तो कई कारकों के कारण अलग-अलग होते हैं, इसलिए यह थोड़ा परीक्षण और त्रुटि है।
    3. आपको आधार डेटा को बार-बार बदलने के बाद भी इसे अपने पास रखने के लिए इंडेक्स अपडेट और / या रिब्यूल्ड करना होगा।
    4. ध्यान रखें कि अनुक्रमणिका, मदद करते समय SELECT, चोट लगी होगी UPDATEक्योंकि यह एक अन्य ऑब्जेक्ट है जिसे उस ऑपरेशन के दौरान अपडेट किया जाना चाहिए, इसलिए I / O। यह फ़िल्टर किए गए इंडेक्स का उपयोग करके दोनों में खेलता है (जो कि कम पंक्तियों को फ़िल्टर से मेल खाने के बाद से आप अपडेट करते हैं), और इंडेक्स को जोड़ने के लिए थोड़ी प्रतीक्षा कर रहे हैं (यदि यह शुरुआत में सुपर सहायक नहीं हो रहा है, तो कोई कारण नहीं है अतिरिक्त I / O)।

अद्यतन: कृपया एक सवाल का मेरा उत्तर देखें, जो इस सुझाव से संबंधित है कि ऊपर दिए गए सुझाव को लागू करने के लिए क्या है, जिसमें स्टेटस को ट्रैक करने और सफाई को रद्द करने के लिए एक तंत्र भी शामिल है: sql सर्वर: छोटे चैंकों में विशाल टेबल पर फ़ील्ड अपडेट करना: कैसे प्राप्त करें प्रगति की स्थिति?


# 4 में आपके सुझाव कुछ मामलों में तेज़ हो सकते हैं, लेकिन लगता है कि जोड़ने के लिए महत्वपूर्ण कोड जटिलता है। मैं सरल शुरू करने का पक्ष लूंगा, और फिर अगर वह आपकी आवश्यकताओं को पूरा नहीं करता है तो विकल्प पर विचार करें।
बेकन बिट्स

@BaconBits सरल शुरू करने पर सहमत हुए। निष्पक्ष होने के लिए, ये सुझाव सभी परिदृश्यों पर लागू होने के लिए नहीं थे। सवाल एक बहुत बड़ी (100 मिलियन + पंक्तियों) तालिका से निपटने के बारे में है।
सोलोमन रटज़की
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.