2011-03-08 12 views
7

मेरे पास एक ऐसी प्रणाली है जिसमें बाहरी सिस्टम के साथ इंटरफेसिंग के लिए एक जटिल प्राथमिक कुंजी है, और आंतरिक उपयोग के लिए एक तेज़, छोटी अपारदर्शी प्राथमिक कुंजी है। उदाहरण के लिए: बाहरी कुंजी एक यौगिक मूल्य हो सकती है - कुछ (नाम दिया गया नाम (वर्कर), पारिवारिक नाम (वर्कर), ज़िप कोड (चार)) और आंतरिक कुंजी एक पूर्णांक ("ग्राहक आईडी") होगी।समेकित रूप से बिना किसी विवाद के 0 जेनरिक एसक्यूएल में नई पंक्ति को पुनर्प्राप्त करें (चुनें) या बनाएं (डालें)

जब मुझे बाहरी कुंजी के साथ आने वाला अनुरोध प्राप्त होता है, तो मुझे आंतरिक कुंजी को देखने की आवश्यकता होती है - और यहां एक मुश्किल हिस्सा है - नया आंतरिक कुंजी आवंटित करें यदि मेरे पास पहले से ही बाहरी आईडी के लिए कोई नहीं है ।

स्पष्ट रूप से यदि मेरे पास एक समय में डेटाबेस से बात करने वाला केवल एक ग्राहक है, तो यह ठीक है। SELECT customer_id FROM customers WHERE given_name = 'foo' AND ..., तो INSERT INTO customers VALUES (...) अगर मुझे कोई मूल्य नहीं मिलता है। लेकिन, अगर बाहरी सिस्टम से संभावित रूप से कई अनुरोध आ रहे हैं, और कई लोग पहले से अनजान ग्राहक के लिए एक बार पहुंच सकते हैं, तो एक दौड़ की स्थिति है जहां कई ग्राहक INSERT नई पंक्ति का प्रयास कर सकते हैं।

यदि मैं मौजूदा पंक्ति को संशोधित कर रहा था, तो यह आसान होगा; करने से पहले, बस SELECT FOR UPDATE पहले, उपयुक्त पंक्ति-स्तर लॉक प्राप्त करने के लिए। लेकिन इस मामले में, मेरे पास एक पंक्ति नहीं है जिसे मैं लॉक कर सकता हूं, क्योंकि पंक्ति अभी तक मौजूद नहीं है!

मैं अब तक कई समाधान के साथ आए है, लेकिन उनमें से प्रत्येक के कुछ बहुत महत्वपूर्ण मुद्दों है:

  1. पकड़ने INSERT पर त्रुटि, ऊपर से पूरे लेन-देन फिर से प्रयास करें। यह एक समस्या है यदि लेनदेन में एक दर्जन ग्राहक शामिल हैं, खासकर यदि आने वाले डेटा संभावित रूप से एक ही ग्राहक के बारे में हर बार एक अलग आदेश में बात कर रहे हैं। पारस्परिक रूप से रिकर्सिव डेडलॉक लूप में फंस जाना संभव है, जहां हर बार एक अलग ग्राहक पर संघर्ष होता है। आप पुन: प्रयास प्रयासों के बीच एक घातीय प्रतीक्षा समय के साथ इसे कम कर सकते हैं, लेकिन संघर्षों से निपटने के लिए यह एक धीमा और महंगा तरीका है। साथ ही, यह एप्लिकेशन कोड को थोड़ा सा जटिल करता है क्योंकि सबकुछ पुनरारंभ करने की आवश्यकता है।
  2. savepoints का उपयोग करें। SELECT से पहले एक सेवपॉइंट प्रारंभ करें, INSERT पर त्रुटि पकड़ें, और उसके बाद सहेजेंपॉइंट और SELECT पर फिर से रोल करें। Savepoints पूरी तरह से पोर्टेबल नहीं हैं, और उनके semantics और क्षमताओं डेटाबेस के बीच थोड़ा और संक्षेप में भिन्न; मैंने देखा है कि सबसे बड़ा अंतर यह है कि, कभी-कभी वे घोंसले लगते हैं और कभी-कभी वे नहीं करते हैं, इसलिए अगर मैं उनसे बच सकता तो यह अच्छा होगा। हालांकि यह केवल एक अस्पष्ट प्रभाव है - क्या यह गलत है? क्या savepoints मानकीकृत, या कम से कम व्यावहारिक रूप से संगत हैं? इसके अलावा, सेवपॉइंट्स समान लेनदेन पर समानांतर में चीजों को करना मुश्किल बनाता है, क्योंकि हो सकता है कि आप यह बताने में सक्षम न हों कि आप कितना काम वापस ले जाएंगे, हालांकि मुझे एहसास है कि मुझे इसके साथ रहने की आवश्यकता हो सकती है।
  3. कुछ वैश्विक लॉक प्राप्त करें, जैसे कि LOCK कथन (oraclemysqlpostgres) का उपयोग करके तालिका-स्तर लॉक की तरह। यह स्पष्ट रूप से इन परिचालनों को धीमा कर देता है और परिणामस्वरूप बहुत सारे लॉक विवाद होते हैं, इसलिए मैं इससे बचना पसंद करूंगा।
  4. अधिक बढ़िया, लेकिन डेटाबेस-विशिष्ट लॉक प्राप्त करें। मैं केवल Postgres's way of doing this से परिचित हूं, जो कि निश्चित रूप से अन्य डेटाबेस में समर्थित नहीं है (फ़ंक्शन भी "pg_" से शुरू होते हैं) तो फिर यह एक पोर्टेबिलिटी समस्या है। इसके अलावा, पोस्टग्रेस के इस तरीके से मुझे कुंजी को किसी भी तरह की पूर्णांक में कनवर्ट करने की आवश्यकता होगी, जो यह अच्छी तरह से फिट नहीं हो सकता है। क्या hypothetical वस्तुओं के लिए ताले हासिल करने के लिए एक अच्छा तरीका है?

ऐसा लगता है कि यह डेटाबेस के साथ एक आम सहमति समस्या होनी चाहिए लेकिन मुझे इस पर बहुत सारे संसाधन नहीं मिल पाए हैं; संभवतः सिर्फ इसलिए कि मैं कैनोलिक phrasing नहीं जानता। क्या किसी भी टैग किए गए डेटाबेस में सिंटैक्स के कुछ सरल अतिरिक्त बिट के साथ ऐसा करना संभव है?

+1

अपरर्ट/मर्ज स्टेटमेंट? –

+0

यदि ग्राहक बाहरी कुंजी का हिस्सा है तो मुझे समस्या दिखाई नहीं दे रही है। – dkretz

+1

यदि प्रश्न ** केवल ** टैग किया गया था 'MySQL' मैं 'INSERT ... पर लागू कुंजी' का उपयोग करने का सुझाव दूंगा। (वैकल्पिक रूप से 'ग्राहक_आईडी = LAST_INSERT_ID (customer_id) के साथ पंक्ति ग्राहक_आईडी पुनर्प्राप्त करने के लिए) –

उत्तर

0

डब्लूआरटी एक अपारदर्शी प्राथमिक कुंजी उत्पन्न करने के लिए, कई विकल्प हैं, उदाहरण के लिए, एक ग्रिड या (कम से कम, ओरेकल के साथ) अनुक्रम तालिका का उपयोग करें। बाहरी कुंजी बीमा करने वाले डब्लूआरटी अद्वितीय है, कॉलम पर अद्वितीय बाधा लागू करें। यदि सम्मिलन विफल रहता है क्योंकि कुंजी मौजूद है, तो fetch को पुनः प्रयास करें। आप एक सम्मिलन का उपयोग कर सकते हैं जहां मौजूद नहीं है या कहां नहीं है। गोल यात्रा को कम करने और प्रदर्शन में सुधार करने के लिए संग्रहीत प्रक्रिया का उपयोग करें।

3

मुझे स्पष्ट नहीं है कि आप इंसर्ट इग्नोर का उपयोग क्यों नहीं कर सकते हैं, जो बिना त्रुटि के चलाएगा और आप जांच सकते हैं कि कोई प्रविष्टि हुई है (संशोधित रिकॉर्ड)। यदि सम्मिलन "विफल रहता है", तो आप जानते हैं कि कुंजी पहले से मौजूद है और आप एक चयन कर सकते हैं। आप पहले INSERT कर सकते हैं, फिर चयन करें।

वैकल्पिक रूप से, यदि आप MySQL का उपयोग कर रहे हैं, तो इनओडीबी का उपयोग करें जो लेनदेन का समर्थन करता है। इससे रोलबैक करना आसान हो जाएगा।

+1

इंसर्ट इग्नोर MySQL- विशिष्ट प्रतीत होता है, हालांकि मैं मानता हूं कि यह वही करता है जो मैं चाहता हूं। क्या कोई और पोर्टेबल विकल्प है? – Glyph

1

मुख्य, बहु-ग्राहक लेनदेन से पहले और बाहर, प्रत्येक ग्राहक के "लुकअप या हो सकता है" ऑपरेशन ऑटोोकॉमिट मोड में करें।

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