2010-11-19 18 views
84

मैं डेटाबेस अखंडता सुनिश्चित करने के लिए लेनदेन बनाम लॉकिंग टेबल के साथ थोड़ा उलझन में हूं और सुनिश्चित करता हूं कि एक चयन और अद्यतन सिंक में रहेगा और कोई अन्य कनेक्शन इसके साथ हस्तक्षेप नहीं करेगा।MySQL: लेनदेन बनाम लॉकिंग टेबल्स

SELECT * FROM table WHERE (...) LIMIT 1 

if (condition passes) { 
    // Update row I got from the select 
    UPDATE table SET column = "value" WHERE (...) 

    ... other logic (including INSERT some data) ... 
} 

मैं यह सुनिश्चित करना है कि कोई अन्य प्रश्नों के हस्तक्षेप और एक ही SELECT (उस कनेक्शन से पहले 'पुराने मूल्य' पढ़ने पंक्ति को अद्यतन करने के समाप्त होने प्रदर्शन करेंगे की जरूरत है

मैं जानता हूँ कि मैं डिफ़ॉल्ट कर सकते हैं: मैं की जरूरत है। LOCK TABLES table पर यह सुनिश्चित करने के लिए कि केवल 1 कनेक्शन एक समय में ऐसा कर रहा है, और जब मैं कर रहा हूं तो इसे अनलॉक कर सकता हूं, लेकिन यह ओवरकिल जैसा लगता है। लेनदेन में इसे लपेटना एक ही काम करेगा (सुनिश्चित करने के लिए कोई अन्य कनेक्शन प्रयास नहीं करता है प्रक्रिया जबकि कोई अन्य अभी भी प्रसंस्करण कर रहा है)? या SELECT ... FOR UPDATE या SELECT ... LOCK IN SHARE MODE बेहतर होगा?

उत्तर

124

लॉकिंग टेबल पंक्तियों/तालिकाओं आप बंद कर दिया है को प्रभावित करने से अन्य डीबी उपयोगकर्ताओं को रोकता है। लेकिन ताले, खुद में, यह सुनिश्चित नहीं करेंगे कि आपका तर्क एक स्थिर राज्य में आता है।

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

$balance = "GET BALANCE FROM your ACCOUNT"; 
if ($balance < $amount_being_paid) { 
    charge_huge_overdraft_fees(); 
} 
$balance = $balance - $amount_being paid; 
UPDATE your ACCOUNT SET BALANCE = $balance; 

$balance = "GET BALANCE FROM receiver ACCOUNT" 
charge_insane_transaction_fee(); 
$balance = $balance + $amount_being_paid 
UPDATE receiver ACCOUNT SET BALANCE = $balance 

अब, कोई ताले और कोई लेनदेन के साथ, इस प्रणाली विभिन्न दौड़ की स्थिति, की सबसे बड़ी के लिए असुरक्षित है जो आपके खाते पर एकाधिक भुगतान किया जा रहा है, या रिसीवर का खाता समानांतर में किया जा रहा है। जबकि आपके कोड में आपकी शेष राशि पुनर्प्राप्त की गई है और यह big_overdraft_fees() और क्या नहीं कर रहा है, यह पूरी तरह से संभव है कि कुछ अन्य भुगतान समानांतर कोड समानांतर में चल रहे होंगे। वे आपकी शेष राशि (कहें, $ 100) प्राप्त करेंगे, अपने लेनदेन करें ($ 20 जो आप भुगतान कर रहे हैं, और $ 30 वे आपको खत्म कर रहे हैं), और अब दोनों कोड पथों में दो अलग-अलग शेष हैं: $ 80 और $ 70। आखिरी बार कौन सा खत्म होता है, इस पर निर्भर करते हुए, आप अपने खाते में उन दो शेषों में से किसी एक के साथ समाप्त हो जाएंगे, $ 50 की बजाय आपको $ 100 - $ 20 - $ 30) समाप्त होना चाहिए था। इस मामले में, "आपके पक्ष में बैंक त्रुटि"।

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

तो ... लेनदेन दर्ज करें। आप एक लेनदेन शुरू करते हैं, आप अपने खाते को $ 20 डेबिट करते हैं, आप रिसीवर को $ 20 के साथ क्रेडिट करने का प्रयास करते हैं ... और कुछ फिर से उड़ाता है। लेकिन इस बार, exit() के बजाय, कोड केवल rollback कर सकता है, और Poof, आपका $ 20 जादुई रूप से आपके खाते में जोड़ा गया है।

अंत में, यह इस करने पर निर्भर करता:

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

कल के पाठ में: डेडलॉक्स की जॉय।

+2

मैं भी/अभी भी उलझन में हूँ। कहें कि रिसीवर खाते में शुरू होने के लिए $ 100 था और हम अपने खाते से $ 20 बिल भुगतान जोड़ रहे हैं। लेनदेन की मेरी समझ यह है कि जब वे शुरू होते हैं, तो किसी भी लेन-देन के संचालन में राज्य में डेटाबेस को लेनदेन की शुरुआत में देखा जाता है। यानी: जब तक हम इसे बदल नहीं देते, रिसीवर खाते में $ 100 है। तो ... जब हम $ 20 जोड़ते हैं तो हम वास्तव में $ 120 का संतुलन निर्धारित करते हैं। लेकिन क्या होता है, अगर हमारे लेनदेन के दौरान, किसी ने रिसीवर खाते को $ 0 तक निकाला? क्या यह किसी भी तरह से रोका गया है? क्या वे जादूगर रूप से $ 120 फिर से प्राप्त करते हैं? क्या ताले की जरूरत है? – Russ

+0

हां, यही वह जगह है जहां ताले खेलते हैं। एक उचित प्रणाली रिकॉर्ड-लॉक करेगी ताकि लेनदेन प्रगति के दौरान कोई भी रिकॉर्ड अपडेट नहीं कर सके। एक पागल प्रणाली रिकॉर्ड पर बिना शर्त लॉक रखेगी ताकि कोई भी "बासी" संतुलन को पढ़ सके। –

+1

असल में लेनदेन को अपने कोड पथ के अंदर चीजों को सुरक्षित करने के रूप में देखें। "समांतर" कोड पथों में सुरक्षित चीजें लॉक करता है। जब तक deadlocks हिट ... –

0

मैं एक

START TRANSACTION WITH CONSISTENT SNAPSHOT; 

के साथ शुरू करने के लिए उपयोग करना चाहते हैं, और एक

COMMIT; 

साथ समाप्त करने के लिए।

यदि आप अपने स्टोरेज इंजन लेनदेन (जो InnoDB है) का समर्थन करता है तो आपके डेटाबेस के अन्य उपयोगकर्ताओं से अलग है।

+1

वह तालिका जिसे वह चुन रहा है उसे छोड़कर अन्य सत्रों तक लॉक नहीं किया जाएगा जब तक कि वह विशेष रूप से इसे लॉक नहीं करता (या जब तक उसका अद्यतन नहीं होता), जिसका अर्थ है कि अन्य सत्र साथ ही चयन और अद्यतन के बीच संशोधित हो सकते हैं। –

+0

MySQL दस्तावेज़ में कॉन्स्टिएंट स्नैपशॉट के साथ स्टार्ट ट्रांज़ेक्शन पर पढ़ने के बाद, मुझे नहीं लगता कि यह वास्तव में एक ही पंक्ति को अपडेट करने से दूसरे कनेक्शन को लॉक करता है। मेरी समझ यह है कि यह देखेंगे कि हालांकि लेनदेन की शुरुआत में तालिका शुरू हुई थी। तो यदि कोई अन्य लेनदेन प्रगति पर है, तो पहले ही एक पंक्ति प्राप्त कर चुकी है और इसे अपडेट करने वाला है, दूसरा लेनदेन अभी भी अपडेट होने से पहले पंक्ति को देखेगा। यह संभावित रूप से उसी लेन-देन को अपडेट करने का प्रयास कर सकता है जो अन्य लेनदेन के बारे में है। क्या यह सही है या क्या मुझे प्रगति में कुछ याद आ रहा है? – Ryan

+1

@Ryan यह कोई लॉकिंग नहीं करता है; तुम सही हो। लॉकिंग (या नहीं) आपके द्वारा किए जाने वाले संचालन के प्रकार (निर्धारित/अद्यतन/हटाएं) द्वारा निर्धारित की जाती है। –

6

IF NOT EXISTS ... का प्रयास करते समय मुझे एक ही समस्या थी और फिर INSERT प्रदर्शन कर रहा था, जिसने रेस स्थिति उत्पन्न की जब एकाधिक थ्रेड एक ही तालिका को अपडेट कर रहे थे।

मैं इस समस्या का हल यहाँ पाया: How to write INSERT IF NOT EXISTS queries in standard SQL

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

+1

+1 महान लिंक वाले आलेख के लिए जो मंच स्वतंत्र है। – Russ

13

आप एक लेनदेन के अंदर SELECT ... FOR UPDATE या SELECT ... LOCK IN SHARE MODE चाहते हैं, जैसा कि आपने कहा था, सामान्य रूप से चयन करता है, इससे कोई फर्क नहीं पड़ता कि वे लेनदेन में हैं या नहीं, एक टेबल लॉक नहीं करेंगे। आप जो भी चुनते हैं वह इस बात पर निर्भर करेगा कि क्या आप अन्य लेन-देन उस पंक्ति को पढ़ने में सक्षम होना चाहते हैं, जबकि आपका लेनदेन प्रगति पर है।

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

START TRANSACTION WITH CONSISTENT SNAPSHOT, आप के लिए चाल से काम नहीं चलेगा के रूप में अन्य लेनदेन अभी भी आकर उस पंक्ति को संशोधित कर सकते हैं। यह नीचे दिए गए लिंक के शीर्ष पर सही है।

तो दूसरे सत्र एक साथ अद्यतन एक ही मेज [...] आप तालिका एक राज्य है कि डेटाबेस में कभी नहीं अस्तित्व में में देख सकते हैं।

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

2

आप लॉक & लेनदेन से उलझन में हैं। वे आरएमडीबी में दो अलग-अलग चीजें हैं। लॉक समवर्ती संचालन को रोकता है जबकि लेनदेन डेटा अलगाव पर केंद्रित होता है। स्पष्टीकरण और कुछ सुंदर समाधान के लिए this महान लेख देखें।

+0

ताले दूसरों को रिकॉर्ड के साथ हस्तक्षेप करने से रोकते हैं जो आप वर्णन कर रहे हैं कि यह संक्षेप में क्या करता है, और लेन-देन बाद की त्रुटियों को रोकता है (दूसरों के समानांतर में परिवर्तन कर रहे हैं) जो आपने पहले की चीजों में हस्तक्षेप से किया है (किसी ने घटना में रोलबैक की अनुमति दी है समानांतर में कुछ) बहुत अधिक लेनदेन बताता है ... इन विषयों की उनकी समझ के बारे में क्या उलझन में है? – steviesama

2

लेनदेन अवधारणाएं और ताले अलग हैं। हालांकि, लेनदेन एसीआईडी ​​सिद्धांतों का पालन करने में मदद करने के लिए ताले का उपयोग किया जाता है। यदि आप पढ़ना/लिखते समय दूसरों को एक ही समय पर पढ़ने/लिखने के लिए तालिका को देखना चाहते हैं, तो आपको ऐसा करने के लिए लॉक की आवश्यकता है। यदि आप डेटा अखंडता और स्थिरता सुनिश्चित करना चाहते हैं, तो आप बेहतर लेनदेन का उपयोग कर सकते थे। मुझे लगता है कि ताले के साथ लेनदेन में अलगाव स्तर की मिश्रित अवधारणाएं। कृपया लेनदेन के अलगाव स्तर खोजें, सर्विसेज वह स्तर होना चाहिए जो आप चाहते हैं।

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