2010-09-29 10 views
42

मेरे पास एसक्यूएल और लॉकिंग रणनीतियों के बारे में एक सवाल है। उदाहरण के तौर पर, मान लीजिए कि मेरी वेबसाइट पर छवियों के लिए एक दृश्य काउंटर है। अगर मैं एक sproc या निम्नलिखित बयानों प्रदर्शन करने के लिए इसी तरह की है:एसक्यूएल परमाणु वृद्धि और लॉकिंग रणनीतियों - क्या यह सुरक्षित है?

START TRANSACTION; 
UPDATE images SET counter=counter+1 WHERE image_id=some_parameter; 
COMMIT; 

मान लें एक विशिष्ट image_id के लिए काउंटर समय t0 में मूल्य '0' है। यदि एक ही छवि काउंटर, एस 1 और एस 2 अपडेट करने वाले दो सत्र, टी 0 पर समवर्ती रूप से शुरू होते हैं, तो क्या कोई मौका है कि इन दो सत्रों में मूल्य '0' पढ़ा गया है, इसे '1' तक बढ़ाएं और दोनों काउंटर को '1' को अपडेट करने का प्रयास करें। ', तो काउंटर को' 2 'के बजाय मूल्य' 1 'मिलेगा?

s1: begin 
s1: begin 
s1: read counter for image_id=15, get 0, store in temp1 
s2: read counter for image_id=15, get 0, store in temp2 
s1: write counter for image_id=15 to (temp1+1), which is 1 
s2: write counter for image_id=15 to (temp2+1), which is also 1 
s1: commit, ok 
s2: commit, ok 

अंत परिणाम: गलत मान '1' image_id = 15 के लिए, 2.

मेरे सवालों का होना चाहिए रहे हैं:

  1. इस परिदृश्य संभव है?
  2. यदि हां, तो लेनदेन अलगाव स्तर क्या मायने रखता है?
  3. क्या कोई संघर्ष समाधानकर्ता है जो इस तरह के संघर्ष को त्रुटि के रूप में पहचानता है?
  4. कोई समस्या से बचने के लिए किसी विशेष वाक्यविन्यास का उपयोग कर सकता है (तुलना और स्वैप (सीएएस) या स्पष्ट लॉकिंग तकनीकों की तरह कुछ)?

मैं एक सामान्य जवाब में दिलचस्पी रखता हूँ, लेकिन अगर कोई भी कर रहे हैं मैं MySQL और InnoDB-विशिष्ट जवाब में दिलचस्पी रखता हूँ, क्योंकि मैं InnoDB पर दृश्यों को लागू करने के लिए इस तकनीक का उपयोग करने के लिए कोशिश कर रहा हूँ।

संपादित करें: निम्नलिखित परिदृश्य भी संभव हो सकता है, जिसके परिणामस्वरूप एक ही व्यवहार हो सकता है। मुझे लगता है कि हम अलगाव स्तर READ_COMMITED या उच्चतर हैं, ताकि एस 2 लेनदेन की शुरुआत से मूल्य प्राप्त हो, हालांकि एस 1 ने काउंटर को पहले ही '1' लिखा है।

s1: begin 
s1: begin 
s1: read counter for image_id=15, get 0, store in temp1 
s1: write counter for image_id=15 to (temp1+1), which is 1 
s2: read counter for image_id=15, get 0 (since another tx), store in temp2 
s2: write counter for image_id=15 to (temp2+1), which is also 1 
s1: commit, ok 
s2: commit, ok 
+0

mysql http://stackoverflow.com/questions/4358732/is-incrementing-a-field-in-mysql-atomic || एमएस http://stackoverflow.com/questions/193257/in-ms-sql- सर्वर-is-there-a-way-to-atomically-increment-a-column-being-used-a –

उत्तर

28

UPDATE क्वेरी पृष्ठों या अद्यतनों पर एक अद्यतन लॉक रखती है।

जब कोई निर्णय किया जाता है कि रिकॉर्ड अपडेट करना है या नहीं, तो लॉक को या तो अनन्य लॉक में उठाया या प्रचारित किया जाता है।

इसका मतलब है कि इस परिदृश्य में: जब तक s1 का फैसला करता है काउंटर या नहीं लिख करे या नहीं और इस परिदृश्य असंभव वास्तव में

s1: read counter for image_id=15, get 0, store in temp1 
s2: read counter for image_id=15, get 0, store in temp2 
s1: write counter for image_id=15 to (temp1+1), which is 1 
s2: write counter for image_id=15 to (temp2+1), which is also 1 

s2 इंतजार करेंगे।

यह इस होगा:

s1: place an update lock on image_id = 15 
s2: try to place an update lock on image_id = 15: QUEUED 
s1: read counter for image_id=15, get 0, store in temp1 
s1: promote the update lock to the exclusive lock 
s1: write counter for image_id=15 to (temp1+1), which is 1 
s1: commit: LOCK RELEASED 
s2: place an update lock on image_id = 15 
s2: read counter for image_id=15, get 1, store in temp2 
s2: write counter for image_id=15 to (temp2+1), which is 2 

ध्यान दें कि InnoDB में, DML प्रश्नों रिकॉर्ड वे पढ़ने से अद्यतन ताले उठा नहीं है।

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

+1

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

+1

क्या एस 1 के लिए प्रारंभिक अपडेट लॉक रखने के लिए आवश्यक न्यूनतम लेनदेन अलगाव स्तर है, जैसा कि उसके उत्तर में ConcernedOfTunbridgeWells द्वारा सुझाया गया है? –

+0

@ डिसाउन: 'एमवीसीसी' में 'पोस्टग्रेएसक्यूएल' का उपयोग करता है, वहां लॉकिंग की कोई अवधारणा नहीं है। इसके बजाय, रिकॉर्डर के एकाधिक संस्करण मार्कर के रूप में लेनदेन पहचानकर्ता का उपयोग करके संग्रहीत किए जाते हैं। लॉकबो में एक संस्करण को संशोधित करने का प्रयास करते समय केवल लॉकिंग होती है। – Quassnoi

8

ताला ठीक से नहीं किया गया है, तो यह निश्चित रूप से इस प्रकार के रेस स्थिति प्राप्त करने के लिए संभव है, और डिफ़ॉल्ट ताला मोड (प्रतिबद्ध पढ़ें) यह अनुमति मिल जाएगी। इस मोड में, पढ़ता है केवल रिकॉर्ड पर साझा लॉक रखता है, इसलिए वे दोनों 0 देख सकते हैं, इसे बढ़ा सकते हैं और डेटाबेस में 1 लिख सकते हैं।

इस दौड़ की स्थिति से बचने के लिए, आपको पढ़ने के ऑपरेशन पर एक विशेष लॉक सेट करने की आवश्यकता है। 'Serializable' और 'दोहराने योग्य पढ़ने' concurrency मोड यह करेंगे, और एक पंक्ति पर एक ऑपरेशन के लिए वे काफी बराबर हैं।

आप के लिए है यह पूरी तरह से परमाणु बनाने के लिए:

  • Serializable के रूप में सेट एक उपयुक्त transaction isolation level इस तरह के। आम तौर पर आप इसे क्लाइंट लाइब्रेरी या एसक्यूएल में स्पष्टीकरण से कर सकते हैं।
  • लेनदेन
  • शुरू पढ़ें डेटा
  • अपडेट यह
  • लेनदेन प्रतिबद्ध होते हैं।

आप अपनी एसक्यूएल बोली के आधार पर होल्डॉक (टी-एसक्यूएल) या समकक्ष संकेत के साथ पढ़ने पर एक विशेष लॉक भी बल दे सकते हैं।

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

ध्यान दें कि हॉट स्पॉट बनाने के बिना ऐसा करने के लिए आपके डेटाबेस को संग्रहीत प्रक्रिया में autonomous (nested) transactions के लिए उचित समर्थन होना चाहिए। ध्यान दें कि कभी-कभी 'नेस्टेड' का उपयोग चेनिंग लेनदेन या बिंदुओं को बचाने के लिए किया जाता है, इसलिए यह शब्द थोड़ा उलझन में हो सकता है। मैंने इसे स्वायत्त लेनदेन के संदर्भ में संपादित किया है।

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

आईआईआरसी ओरेकल स्वायत्त लेनदेन का समर्थन करता है लेकिन डीबी/2 काफी हाल ही में नहीं हुआ और SQL सर्वर नहीं करता है। मेरे सिर के ऊपर से मुझे नहीं पता कि क्या InnoDB उनका समर्थन करता है, लेकिन Grey and Reuter कुछ हद तक आगे बढ़ता है कि उन्हें कितना मुश्किल लागू करना है। अभ्यास में मुझे लगता है कि यह काफी संभावना है कि ऐसा नहीं हो सकता है। YMMV।

+0

एकल 'अद्यतन के साथ 'प्रश्न, लेन-देन का समर्थन करने वाले किसी भी प्रमुख सिस्टम में इस दौड़ की स्थिति को प्राप्त करना संभव नहीं है, इससे कोई फर्क नहीं पड़ता कि कौन सा लेनदेन अलगाव स्तर का उपयोग किया जाता है। – Quassnoi

+0

यदि मैं समस्या को सही ढंग से समझता हूं, तो अलगाव होने के लिए पर्याप्त नहीं है, लेकिन हो सकता है कि स्नैपशॉट अलगाव (http://en.wikipedia.org/wiki/Snapshot_isolation), क्योंकि मुझे ' इस मामले में संघर्ष '। क्या दो संघर्ष में हैं या नहीं? और यदि आपके पास अद्यतन के दौरान पंक्ति पर एक विशेष लॉक है, तो क्या होगा यदि s2: s अद्यतन s1: s अद्यतन और प्रतिबद्धता के बीच आता है?एस 2 फिर क्या पढ़ेगा? तर्कसंगत 0. एकमात्र तरीका यह है कि मैं यह सुनिश्चित करने के लिए देख सकता हूं कि इस मामले में सही व्यवहार एस 1 के लिए अपने प्रतिबद्ध होने तक अनन्य लॉक पकड़ने के लिए होगा। –

+0

@Quassnoi: अद्यतन के बाद और प्रतिबद्ध होने से पहले एक और लेनदेन क्या पढ़ता है? यदि आपके पास उदाहरण के लिए READ_COMMITED है, तो अन्य लेनदेन को मूल्य '1' देखें, गलत होगा (अन्य लेनदेन में देखेगा)। '0' एकमात्र अन्य तार्किक मूल्य है कि अन्य समवर्ती लेनदेन देखने में सक्षम होना चाहिए। तो या तो यह दौड़ की स्थिति होने में सक्षम होना चाहिए (और संभवतः एक गलती हो सकती है), या डीबी को किसी भी तरह लेनदेन को क्रमबद्ध करने की आवश्यकता है। –