2008-11-05 25 views
10

आप MySQL में दौड़ की स्थिति को कैसे रोकते हैं? हाथ में समस्या एक सरल एल्गोरिथ्म के कारण होता है:mysql डालने की दौड़ स्थिति

  1. मेज से एक पंक्ति का चयन
  2. अगर यह मौजूद नहीं है, यह

और सम्मिलित तो या तो आप एक डुप्लिकेट पंक्ति मिलता है, या यदि आप इसे अद्वितीय/प्राथमिक कुंजी के माध्यम से रोकते हैं, तो एक त्रुटि।

अब आम तौर पर मुझे लगता है कि लेनदेन यहां मदद करते हैं, लेकिन क्योंकि पंक्ति मौजूद नहीं है, लेनदेन वास्तव में मदद नहीं करता है (या मुझे कुछ याद आ रही है?)।

लॉक टेबल एक ओवरकिल की तरह लगता है, खासकर यदि तालिका प्रति सेकंड कई बार अपडेट की जाती है।

एकमात्र अन्य समाधान जो मैं सोच सकता हूं वह हर अलग आईडी के लिए GET_LOCK() है, लेकिन क्या कोई बेहतर तरीका नहीं है? क्या यहां कोई स्केलेबिलिटी समस्या भी नहीं है? और साथ ही, प्रत्येक तालिका के लिए यह करना थोड़ा अप्राकृतिक लगता है, क्योंकि यह मुझे उच्च-समेकन डेटाबेस में एक बहुत ही आम समस्या की तरह लगता है।

उत्तर

9

आप क्या चाहते हैं LOCK TABLES

है या कि अगर जरूरत से ज्यादा लगता है कि कैसे के बारे में INSERT IGNORE एक चेक के साथ उस पंक्ति वास्तव में डाला गया था।

आप कीवर्ड ध्यान न दें उपयोग करते हैं, त्रुटियों कि जब सम्मिलित करें बयान को क्रियान्वित होते हैं चेतावनी के रूप में इलाज कर रहे हैं बजाय।

+0

इंसर्ट इग्नोर के साथ जाएं। ट्रिगर्स बहुत अधिक ओवरहेड का कारण बनते हैं, वे तब तक इसके लायक नहीं हैं जब तक कि व्यवसाय तर्क जटिल न हो। – captainspi

+0

अनन्य बाधा और पकड़ने वाली त्रुटियों के साथ बस 'INSERT' के साथ क्या गलत है "ठीक है, रिकॉर्ड पहले से मौजूद है।" – KeatsKelleher

4

ऐसा लगता है कि आपके आईडी कॉलम पर एक अनन्य अनुक्रमणिका होनी चाहिए, इसलिए एक दोहराया सम्मिलन फिर से अंधेरे से स्वीकार किए जाने के बजाय त्रुटि उत्पन्न करेगा।

आईडी को प्राथमिक कुंजी के रूप में परिभाषित करके या एक अद्वितीय इंडेक्स का उपयोग करके किया जा सकता है।

मुझे लगता है कि आपको पहले सवाल पूछने की ज़रूरत है कि आपके पास सटीक समान काम करने के कई धागे क्यों हैं? उन्हें एक ही पंक्ति को क्यों डालना होगा?

उत्तर देने के बाद, मुझे लगता है कि त्रुटियों को अनदेखा करना सबसे अधिक सक्षम समाधान होगा, लेकिन दोनों दृष्टिकोणों को मापें (GET_LOCK v/s त्रुटियों को अनदेखा करें) और स्वयं को देखें।

कोई अन्य तरीका नहीं है जिसे मैं जानता हूं। आप त्रुटियों से बचना क्यों चाहते हैं? किसी अन्य प्रकार की त्रुटि होने पर आपको अभी भी उस मामले के लिए कोड करना होगा।

जैसा कि सांख्यिकीविद कहते हैं कि लेन-देन मदद करते हैं, लेकिन जैसा कि वे आमतौर पर निहित होते हैं, यदि दो आवेषण अलग-अलग धागे से भागते हैं, तो वे दोनों एक अंतर्निहित लेनदेन के अंदर होंगे और डेटाबेस के निरंतर विचार देखेंगे।

+0

वैसे हां, हमारे पास अद्वितीय इंडेस आदि हैं ... असल में यही कारण है कि हमें यह पता चला है कि समस्या मौजूद है, अद्वितीय इंडेक्स द्वारा त्रुटियों को ट्रिगर किया गया है। – tpk

+2

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

2

तकनीकी स्तर पर, एक लेनदेन यहां सहायता करेगा क्योंकि अन्य धागे लेनदेन करने तक नई पंक्ति नहीं देख पाएंगे।

लेकिन व्यवहार जो ऐसा नहीं करता में हल समस्या - यह केवल चाल यह। आपके आवेदन को अब यह जांचने की आवश्यकता है कि प्रतिबद्धता विफल हो जाती है और तय करें कि क्या करना है। मैं आमतौर पर आपके द्वारा किए गए रोलबैक को करता हूं, और लेनदेन को पुनरारंभ करता हूं क्योंकि अब पंक्ति दिखाई देगी।लेनदेन-आधारित प्रोग्रामर को काम करना चाहिए।

+0

हालांकि यह ध्यान देने योग्य है कि लेन-देन (अलगाव के स्तर के आधार पर और इसी तरह) लॉकिंग भी शामिल है, डेटाबेस को बुनियादी ढांचे में लॉक करने से बेहतर है ताकि इसे स्वयं कॉल किया जा सके। –

0

मैं एक ही समस्या में पड़ गए और एक पल :) के लिए नेट की खोज

अंत में मैं creating filesystem objects in shared (temporary) directories to securely open temporary files:

$exists = $success = false; 
do{ 
$exists = check();// select a row in the table 
if (!$exists) 
    $success = create_record(); 
    if ($success){ 
    $exists = true; 
    }else if ($success != ERROR_DUP_ROW){ 
    log_error("failed to create row not 'coz DUP_ROW!"); 
    break; 
    }else{ 
    //probably other process has already created the record, 
    //so try check again if exists 
    } 
}while(!$exists) 

को विधि के समान समाधान के साथ आया था busy- की डरो मत लूप - आमतौर पर यह एक या दो बार निष्पादित होगा।

3

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

कुछ गैर-एसक्यूएल सिस्टम आपको एक ही कथन में दोनों (1) और (2) करने की अनुमति देते हैं, आपके ओएस से उत्पन्न होने वाली संभावित दौड़ की स्थिति (1) और (2) के बीच आपके निष्पादन धागे को निलंबित करने की संभावित दौड़ स्थितियों का अर्थ है), पूरी तरह से समाप्त कर रहे हैं।

फिर भी, भविष्य के ताले की अनुपस्थिति में ऐसी प्रणालियों को अभी भी किसी प्रकार की लॉकिंग योजना का सहारा लेना होगा, और ताले के "ग्रैन्युलरिटी" (/ "स्कोप") को बेहतर समेकित करने के लिए बेहतर होगा।

(और समाप्त करने के लिए: कुछ डीबीएमएस की - विशेष रूप से आप के लिए भुगतान करने के लिए नहीं है - वास्तव में ऐसा "पूरे तालिका" के अलावा कोई बेहतर ताला के विवरण का स्तर प्रदान करते हैं।)

+2

+1 "पेपर पर मुद्रित को छोड़कर जो अकादमिक अध्ययन प्रकाशित होते हैं" – Johan

0

तुम बहुत बस द्वारा डुप्लीकेट पंक्तियों को रोकने के अपनी टेबल पर अद्वितीय इंडेक्स डालना। इसमें LOCKS या ट्रांज़ेक्शन के साथ कुछ लेना देना नहीं है।

क्या आपको कोई ख्याल है कि कोई प्रविष्टि विफल हो जाती है क्योंकि यह डुप्लिकेट है? अगर यह विफल रहता है तो आपको अधिसूचित होने की आवश्यकता है? या यह सब मायने रखता है कि पंक्ति डाली गई थी, और इससे कोई फर्क नहीं पड़ता कि कितने या कितने डुप्लिकेट आवेषण विफल रहे?

यदि आपको परवाह नहीं है, तो आपको केवल INSERT IGNORE की आवश्यकता है। लेनदेन या टेबल ताले के बारे में सोचने की कोई ज़रूरत नहीं है।

इनो डीबी में स्वचालित रूप से पंक्ति स्तर लॉक हो रहा है, लेकिन यह केवल अपडेट और हटाए जाने पर लागू होता है। आप सही हैं कि यह आवेषण पर लागू नहीं होता है। आप लॉक नहीं कर सकते जो अभी तक मौजूद नहीं है!

आप पूरी तालिका LOCK स्पष्ट रूप से कर सकते हैं। लेकिन यदि आपका उद्देश्य डुप्लीकेट को रोकने के लिए है, तो आप इसे गलत कर रहे हैं। फिर, एक अद्वितीय सूचकांक का उपयोग करें।

यदि परिवर्तनों का एक सेट बनाया गया है और आप सभी या कुछ भी नतीजे नतीजे (या यहां तक ​​कि सभी या कुछ भी नतीजे के परिणाम को बड़े या कुछ भी नतीजे के परिणाम के भीतर) चाहते हैं, तो लेनदेन का उपयोग करें और savepoints। फिर ROLLBACK या ROLLBACK TO SAVEPOINT *savepoint_name* का उपयोग पूर्ववत करने के लिए हटाएं, जिसमें और प्रविष्टियां शामिल हैं।

LOCK टेबल लेनदेन के प्रतिस्थापन नहीं है, लेकिन यह MyISAM तालिकाओं के साथ आपका एकमात्र विकल्प है, जो लेनदेन का समर्थन नहीं करता है। यदि पंक्ति-स्तर स्तर लॉकिंग पर्याप्त नहीं है तो आप इन्हें इनो डीबी टेबल के साथ भी उपयोग कर सकते हैं। लॉक टेबल स्टेटमेंट के साथ लेनदेन का उपयोग करने के बारे में अधिक जानकारी के लिए this page देखें।

0

मेरे पास एक समान समस्या है। मेरे पास एक सारणी है कि ज्यादातर परिस्थितियों में एक अद्वितीय टिकट_आईडी मूल्य होना चाहिए, लेकिन कुछ ऐसे मामले हैं जहां मेरे पास डुप्लिकेट होगा; सबसे अच्छा डिजाइन नहीं है, लेकिन यह वही है।

  1. उपयोगकर्ता A चेकों देखने के लिए अगर टिकट आरक्षित है, यह
  2. उपयोगकर्ता B चेकों देखने के लिए अगर टिकट आरक्षित है नहीं है, यह नहीं है
  3. उपयोगकर्ता B एक 'आरक्षित' रिकॉर्ड सम्मिलित करता है उस टिकट के लिए तालिका में
  4. उपयोगकर्ता ए उस टिकट के लिए तालिका में 'आरक्षित' रिकॉर्ड डालता है
  5. उपयोगकर्ता बी डुप्लिकेट के लिए जांचें? हाँ, मेरा रिकॉर्ड नया है? हां, इसे छोड़ दें
  6. उपयोगकर्ता डुप्लिकेट के लिए एक चेक? हाँ, मेरा रिकॉर्ड नया है? नहीं, इसे हटाएं

उपयोगकर्ता बी ने टिकट आरक्षित किया है, उपयोगकर्ता ए रिपोर्ट करता है कि टिकट किसी और द्वारा लिया गया है।

मेरे उदाहरण में कुंजी यह है कि आपको टाई ब्रेकर की आवश्यकता है, मेरे मामले में यह पंक्ति पर ऑटो-वृद्धि आईडी है।

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