Database
 sql >> डेटाबेस >  >> RDS >> Database

कृपया इस यूपीएसईआरटी विरोधी पैटर्न का प्रयोग बंद करें

मुझे लगता है कि MERGE . के बारे में मेरी राय पहले से ही सभी जानते हैं और मैं इससे दूर क्यों रहता हूं। लेकिन यहां एक और (एंटी-) पैटर्न है जो मुझे हर जगह दिखाई देता है जब लोग एक अप्सर्ट करना चाहते हैं (यदि यह मौजूद है तो एक पंक्ति अपडेट करें और यदि यह नहीं है तो इसे डालें):

अगर मौजूद है (dbo.t से 1 चुनें जहां [कुंजी] =@ कुंजी) अद्यतन dbo.t सेट वैल =@val जहां [कुंजी] =@ कुंजी; ENDELSEBEGIN INSERT dbo.t ([कुंजी], वैल) VALUES(@key, @val); END

यह एक बहुत ही तार्किक प्रवाह की तरह दिखता है जो दर्शाता है कि हम वास्तविक जीवन में इस बारे में कैसे सोचते हैं:

  • क्या इस कुंजी के लिए कोई पंक्ति पहले से मौजूद है?
    • हां :ठीक है, उस पंक्ति को अपडेट करें।
    • नहीं :ठीक है, फिर इसे जोड़ें।

लेकिन यह बेकार है।

यह पुष्टि करने के लिए पंक्ति का पता लगाना कि यह मौजूद है, केवल इसे अपडेट करने के लिए इसे फिर से ढूंढना है, दो बार काम कर रहा है मुफ्त में। भले ही कुंजी अनुक्रमित हो (जो मुझे आशा है कि हमेशा ऐसा ही होता है)। अगर मैं इस तर्क को एक प्रवाह चार्ट में डाल दूं और प्रत्येक चरण पर, डेटाबेस के भीतर होने वाले ऑपरेशन के प्रकार को जोड़ दूं, तो मेरे पास यह होगा:

ध्यान दें कि सभी पथों में दो इंडेक्स ऑपरेशन होंगे।

इससे भी महत्वपूर्ण बात यह है कि प्रदर्शन एक तरफ, जब तक आप दोनों एक स्पष्ट लेनदेन का उपयोग नहीं करते हैं और अलगाव स्तर को ऊपर नहीं उठाते हैं, जब पंक्ति पहले से मौजूद नहीं है तो कई चीजें गलत हो सकती हैं:

  • यदि कुंजी मौजूद है और दो सत्र एक साथ अपडेट करने का प्रयास करते हैं, तो वे दोनों सफलतापूर्वक अपडेट हो जाएंगे (एक "जीत" जाएगा; "हारने वाला" उस परिवर्तन के साथ पालन करेगा जो चिपक जाता है, जिससे "खोया हुआ अपडेट" हो जाता है)। यह अपने आप में कोई समस्या नहीं है, और हमें इसी तरह चाहिए काम करने के लिए समवर्ती प्रणाली की अपेक्षा करें। पॉल व्हाइट यहां आंतरिक यांत्रिकी के बारे में अधिक विस्तार से बात करते हैं, और मार्टिन स्मिथ यहां कुछ अन्य बारीकियों के बारे में बात करते हैं।
  • यदि कुंजी मौजूद नहीं है, लेकिन दोनों सत्र एक ही तरह से अस्तित्व की जांच करते हैं, तो कुछ भी हो सकता है जब वे दोनों सम्मिलित करने का प्रयास करते हैं:
    • गतिरोध असंगत तालों के कारण;
    • प्रमुख उल्लंघन त्रुटियां बढ़ाएं जो नहीं होना चाहिए था; या,
    • डुप्लिकेट कुंजी मान डालें अगर वह कॉलम ठीक से सीमित नहीं है।

वह आखिरी वाला सबसे खराब है, IMHO, क्योंकि यह वही है जो संभावित रूप से डेटा को दूषित करता है . गतिरोध और अपवादों को त्रुटि प्रबंधन, XACT_ABORT जैसी चीज़ों से आसानी से नियंत्रित किया जा सकता है , और तर्क का पुनः प्रयास करें, इस पर निर्भर करता है कि आप कितनी बार टकराव की अपेक्षा करते हैं। लेकिन अगर आप सुरक्षा की भावना में डूबे हुए हैं कि IF EXISTS चेक आपको डुप्लीकेट (या प्रमुख उल्लंघन) से बचाता है, यह एक आश्चर्य की बात है। यदि आप किसी कॉलम से कुंजी की तरह कार्य करने की अपेक्षा करते हैं, तो उसे आधिकारिक बनाएं और एक बाधा जोड़ें।

"कई लोग कह रहे हैं..."

डैन गुज़मैन ने रेस कंडीशन के बारे में एक दशक से भी पहले कंडीशनल INSERT/UPDATE रेस कंडीशन में और बाद में "UPSERT" रेस कंडीशन विद MERGE में बात की थी।

माइकल स्वार्ट ने भी इस विषय पर कई बार विचार किया है:

  • मिथबस्टिंग:समवर्ती अपडेट/समाधान सम्मिलित करें - जहां उन्होंने स्वीकार किया कि प्रारंभिक तर्क को जगह में छोड़कर और केवल अलगाव स्तर को ऊपर उठाने से मुख्य उल्लंघनों को गतिरोध में बदल दिया गया;
  • मर्ज स्टेटमेंट से सावधान रहें - जहां उन्होंने MERGE . के बारे में अपने उत्साह की जांच की; और,
  • यदि आप मर्ज का उपयोग करना चाहते हैं तो क्या न करें - जहां उन्होंने एक बार फिर पुष्टि की कि MERGE से बचने के लिए अभी भी बहुत सारे वैध कारण हैं ।

सुनिश्चित करें कि आपने तीनों पोस्ट की सभी टिप्पणियों को भी पढ़ लिया है।

समाधान

मैंने अपने करियर में कई गतिरोधों को केवल निम्नलिखित पैटर्न में समायोजित करके तय किया है (अनावश्यक चेक को हटा दें, लेन-देन में अनुक्रम लपेटें, और उचित लॉकिंग के साथ पहली टेबल एक्सेस की रक्षा करें):

लेनदेन शुरू करें; अद्यतन dbo.t के साथ (UPDLOCK, SERIALIZABLE) सेट वैल =@val जहां [कुंजी] =@key; IF @@ ROWCOUNT =0BEGIN INSERT dbo.t([key], val) VALUES(@key, @val);END COMMIT TRANSACTION;

हमें दो संकेतों की आवश्यकता क्यों है? UPDLOCK नहीं है पर्याप्त?

  • UPDLOCK कथन . पर रूपांतरण गतिरोध से बचाने के लिए उपयोग किया जाता है स्तर (पीड़ित को पुन:प्रयास करने के लिए प्रोत्साहित करने के बजाय एक और सत्र प्रतीक्षा करने दें)।
  • SERIALIZABLE लेनदेन . के दौरान अंतर्निहित डेटा में होने वाले परिवर्तनों से बचाने के लिए उपयोग किया जाता है (सुनिश्चित करें कि जो पंक्ति मौजूद नहीं है वह मौजूद नहीं है)।

यह थोड़ा और कोड है, लेकिन यह 1000% सुरक्षित है, और यहां तक ​​कि सबसे खराब में भी मामला (पंक्ति पहले से मौजूद नहीं है), यह एंटी-पैटर्न के समान कार्य करता है। सर्वोत्तम स्थिति में, यदि आप पहले से मौजूद किसी पंक्ति को अपडेट कर रहे हैं, तो उस पंक्ति को केवल एक बार ढूँढना अधिक कुशल होगा। इस तर्क को डेटाबेस में होने वाले उच्च-स्तरीय संचालन के साथ जोड़ना, यह थोड़ा आसान है:

इस मामले में, एक पथ में केवल एक इंडेक्स ऑपरेशन होता है।

लेकिन फिर से, प्रदर्शन एक तरफ:

  • यदि कुंजी मौजूद है और दो सत्र एक ही समय में इसे अपडेट करने का प्रयास करते हैं, तो वे दोनों बारी-बारी से पंक्ति को सफलतापूर्वक अपडेट करेंगे , पहले की तरह।
  • यदि कुंजी मौजूद नहीं है, तो एक सत्र "जीत" जाएगा और पंक्ति सम्मिलित करेगा . दूसरे को इंतजार करना होगा जब तक ताले अस्तित्व की जांच करने के लिए जारी नहीं किए जाते हैं, और अद्यतन करने के लिए मजबूर हो जाते हैं।

दोनों ही मामलों में, रेस जीतने वाला लेखक अपने डेटा को उनके बाद अपडेट किए गए "हारे हुए" के लिए खो देता है।

ध्यान दें कि अत्यधिक समवर्ती सिस्टम पर समग्र थ्रूपुट हो सकता है पीड़ित हैं, लेकिन यह एक ऐसा समझौता है जिसे करने के लिए आपको तैयार रहना चाहिए। आपको बहुत सारे गतिरोध के शिकार या प्रमुख उल्लंघन त्रुटियां मिल रही हैं, लेकिन वे जल्दी से हो रही हैं, यह एक अच्छा प्रदर्शन मीट्रिक नहीं है। कुछ लोग सभी परिदृश्यों से हटाए गए सभी अवरोधों को देखना पसंद करेंगे, लेकिन उनमें से कुछ आपको डेटा अखंडता के लिए पूरी तरह से अवरुद्ध कर रहे हैं।

लेकिन क्या होगा अगर किसी अपडेट की संभावना कम हो?

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

लेनदेन शुरू करें; INSERT dbo.t ([कुंजी], वैल) चुनें @key, @val जहां मौजूद नहीं है ( dbo.t से 1 चुनें (UPDLOCK, SERIALIZABLE) जहां [कुंजी] =@key); IF @@ ROWCOUNT =0BEGIN UPDATE dbo.t SET val =@val WHERE [key] =@key;END COMMIT TRANSACTION;

"जस्ट डू इट" दृष्टिकोण भी है, जहां आप आँख बंद करके सम्मिलित करते हैं और टकराव को कॉलर को अपवाद देते हैं:

लेनदेन शुरू करें; INSERT dbo.t([key], val) VALUES(@key, @val);END TRYBEGIN CATCH UPDATE dbo.t SET val =@val WHERE [key] =@key;END CATCH COMMIT TRANSACTION;> 

उन अपवादों की लागत अक्सर पहले जाँच की लागत से अधिक होगी; आपको इसे हिट/मिस रेट के मोटे तौर पर सटीक अनुमान के साथ आज़माना होगा। मैंने इसके बारे में यहाँ और यहाँ लिखा है।

एकाधिक पंक्तियों को ऊपर करने के बारे में क्या?

उपरोक्त सिंगलटन इंसर्ट/अपडेट निर्णयों से संबंधित है, लेकिन जस्टिन पीलिंग ने पूछा कि जब आप एक से अधिक पंक्तियों को संसाधित कर रहे हैं तो क्या करना है, यह जाने बिना कि उनमें से कौन पहले से मौजूद है?

मान लें कि आप तालिका-मूल्यवान पैरामीटर जैसी किसी चीज़ का उपयोग करने के लिए पंक्तियों का एक सेट भेज रहे हैं, तो आप एक जॉइन का उपयोग करके अपडेट करेंगे, और फिर NOT EXISTS का उपयोग करके सम्मिलित करेंगे, लेकिन पैटर्न अभी भी ऊपर के पहले दृष्टिकोण के बराबर होगा:

क्रिएट प्रोसीजर dbo.UpsertTheThings @tvp dbo.TableType READONLYASBEGIN SET NOCOUNT ON; लेनदेन शुरू करें; अद्यतन टी के साथ (अपडलॉक, सीरियल) सेट वैल =tvp.val dbo.t से टी इनर जॉइन @tvp के रूप में टीवी पर टी। [कुंजी] =टीवीपी। [कुंजी]; INSERT dbo.t ([कुंजी], वैल) चुनें [कुंजी], टीवी के रूप में @tvp से वैल जहां मौजूद नहीं है (dbo.t से 1 चुनें जहां [कुंजी] =टीवीपी। [कुंजी]); प्रतिबद्ध लेनदेन;END

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

निष्कर्ष

ये अपरर्ट पैटर्न उन लोगों से बेहतर हैं जिन्हें मैं अक्सर देखता हूं, और मुझे आशा है कि आप उनका उपयोग करना शुरू कर देंगे। हर बार जब मैं IF EXISTS . खोजता हूं तो मैं इस पोस्ट को इंगित करूंगा जंगली में पैटर्न। और, हे, पॉल व्हाइट के लिए एक और चिल्लाहट (sql.kiwi | @SQK_Kiwi), क्योंकि वह कठिन अवधारणाओं को समझने में आसान बनाने और बदले में समझाने में बहुत उत्कृष्ट हैं।

और अगर आपको लगता है कि आपको करना है MERGE का उपयोग करें , कृपया मुझे @ मत करो; या तो आपके पास एक अच्छा कारण है (हो सकता है कि आपको कुछ अस्पष्ट MERGE . की आवश्यकता हो) -only कार्यक्षमता), या आपने उपरोक्त लिंक को गंभीरता से नहीं लिया।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. पंक्ति लक्ष्य, भाग 3:विरोधी शामिल हों

  2. QUOTENAME फ़ंक्शन के साथ डायनामिक पिवट टेबल बनाना

  3. 19 डेटाबेस डिजाइन त्रुटियों के बारे में सीखने के लिए ऑनलाइन संसाधन

  4. एक डेटाबेस क्या है? और एक डीबीएमएस?

  5. Ubuntu 20.04 पर अरंगोडीबी कैसे स्थापित करें?