MySQL में आशावादी लॉकिंग को सही तरीके से कैसे लागू किया जाए


13

MySQL में आशावादी लॉकिंग को सही तरीके से कैसे लागू किया जाता है?

हमारी टीम ने यह घटाया है कि हमें नीचे # 4 करना होगा वरना एक जोखिम है कि एक और धागा रिकॉर्ड के एक ही संस्करण को अपडेट कर सकता है, लेकिन हम यह सत्यापित करना चाहेंगे कि यह करने का सबसे अच्छा तरीका है।

  1. आप जिस स्तंभ नाम = "संस्करण" के लिए आशावादी लॉकिंग का उपयोग करना चाहते हैं उस तालिका पर एक संस्करण फ़ील्ड बनाएं
  2. चयन करने पर, संस्करण कॉलम को शामिल करना और संस्करण का नोट बनाना सुनिश्चित करें
  3. रिकॉर्ड के बाद के अपडेट के लिए, अपडेट स्टेटमेंट में "कहां वर्जन = एक्स" जारी करना चाहिए, जहां एक्स वह वर्जन है जो हमने # 2 में प्राप्त किया है और उस अपडेट स्टेटमेंट के दौरान एक्स + 1 में वर्जन फील्ड सेट करें
  4. SELECT FOR UPDATEहम जिस रिकॉर्ड को अपडेट करने जा रहे हैं, उस पर प्रदर्शन करें ताकि हम अनुक्रमित करें कि हम उस रिकॉर्ड में बदलाव कर सकते हैं जिसे हम अपडेट करने की कोशिश कर रहे हैं।

स्पष्ट करने के लिए, हम दो थ्रेड्स को रोकने की कोशिश कर रहे हैं जो एक ही समय विंडो में एक ही रिकॉर्ड का चयन करते हैं जहां वे रिकॉर्ड के एक ही संस्करण को एक-दूसरे से अधिलेखित करते हैं यदि वे एक ही समय में रिकॉर्ड को अपडेट करने और अपडेट करने के लिए थे। हम मानते हैं कि जब तक हम # 4 नहीं करते हैं, एक मौका है, कि अगर दोनों धागे एक ही समय में अपने संबंधित लेनदेन में प्रवेश करते हैं (लेकिन अभी तक अपने अपडेट जारी नहीं किए हैं), जब वे अपडेट करने जाते हैं, तो दूसरा धागा जो UPDATE का उपयोग करेगा ... जहां संस्करण = X पुराने डेटा पर काम कर रहा होगा।

क्या हम यह सोचकर सही हैं कि हम इस निराशावादी लॉकिंग को अपडेट करते समय करना चाहिए, भले ही हम संस्करण फ़ील्ड / आशावादी लॉकिंग का उपयोग कर रहे हों?


समस्या क्या है? आप अपने UPDATE के साथ संस्करण संख्या में वृद्धि करते हैं, तो दूसरा UPDATE विफल हो जाएगा क्योंकि संस्करण संख्या वैसी नहीं है जब यह पढ़ा गया था - जो आप चाहते हैं।
आंद्रेकेआर

आप विश्वस्त हैं? यह स्पष्ट नहीं है कि जब तक आप एक विशिष्ट सेटिंग के लिए लेन-देन अलगाव स्तर निर्धारित नहीं करते हैं जो कि आप वास्तव में अन्य थ्रेड्स अद्यतन देखेंगे। यदि आप दोनों एक ही समय में लेन-देन दर्ज करते हैं, तो दूसरा थ्रेड बहुत अच्छी तरह से ओएलडी डेटा देख सकता है जब यह अपडेट करने के लिए जाता है। ओरेकल के अनुसार MySQL ACID के क्षेत्र में उतनी मजबूत नहीं है, इसलिए MySQL में आशावादी लॉकिंग को लागू करने के सर्वोत्तम तरीकों की तलाश है जो गंदे रीड / अपडेट को रोक देगा।
बेस्टप्रैक्टिस

लेकिन फिर भी प्रतिबद्ध होने के दौरान लेनदेन विफल हो जाएगा, है ना?
आंद्रेकेआर

संकेत हैं कि कोई इस स्थिति से निपटने के लिए अपडेट के लिए चयन करना चाहेगा
BestPractices

@BestPractices आपको पंक्ति वर्जन द्वारा या तो SELECT ... FOR UPDATE या आशावादी लॉकिंग की आवश्यकता है , दोनों नहीं। विस्तार से देखें उत्तर
क्रेग रिंगर

जवाबों:


17

आपका डेवलपर गलत है। आपको या तो पंक्तिबद्ध करने की आवश्यकता है , दोनों की नहीं।SELECT ... FOR UPDATE

यह कोशिश करो और देखो। तीन MySQL सत्र खोलें (A), (B)और (C)एक ही डेटाबेस में।

में (C)मुद्दा:

CREATE TABLE test(
    id integer PRIMARY KEY,
    data varchar(255) not null,
    version integer not null
);
INSERT INTO test(id,data,version) VALUES (1,'fred',0);
BEGIN;
LOCK TABLES test WRITE;

दोनों में (A)और वह परीक्षण (B)जारी करता UPDATEहै और पंक्ति संस्करण सेट करता है, winnerप्रत्येक में पाठ को बदलकर आप यह देख सकते हैं कि कौन सा सत्र है:

-- In (A):

BEGIN;
UPDATE test SET data = 'winnerA',
            version = version + 1
WHERE id = 1 AND version = 0;

-- in (B):

BEGIN;
UPDATE test SET data = 'winnerB',
            version = version + 1
WHERE id = 1 AND version = 0;

अब में (C), UNLOCK TABLES;ताला जारी करने के लिए।

(A)और (B)पंक्ति लॉक के लिए दौड़ होगी। उनमें से एक जीत जाएगा और ताला प्राप्त करेगा। दूसरा ताला पर रोक देगा। जिस विजेता को ताला मिला है, वह पंक्ति को बदलने के लिए आगे बढ़ेगा। यह मानते हुए (A)कि आप विजेता हैं, अब आप परिवर्तित पंक्ति देख सकते हैं (फिर भी बिना किसी अन्य लेन-देन के नहीं देखा जा सकता है) SELECT * FROM test WHERE id = 1

अब COMMITविजेता सत्र में, कहते हैं (A)

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

यहाँ pastebin पर सत्र लॉग देखें । मैंने mysql --prompt="A> "सत्रों के बीच अंतर बताना आसान बनाने के लिए आदि का उपयोग किया । मैंने समय अनुक्रम में आउटपुट को कॉपी और पेस्ट किया था, इसलिए यह पूरी तरह से कच्चा आउटपुट नहीं है और यह संभव है कि मैं इसे कॉपी और पेस्ट करने में त्रुटियां कर सकता हूं। इसे स्वयं देखें।


यदि आपने एक पंक्ति संस्करण फ़ील्ड नहीं जोड़ा है, तो आपको SELECT ... FOR UPDATEक्रमबद्ध रूप से ऑर्डर सुनिश्चित करने में सक्षम होने की आवश्यकता होगी ।

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

का उद्देश्य SELECT ... FOR UPDATEहै:

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

आपको आशावादी लॉकिंग (पंक्ति संस्करण) और दोनों का उपयोग करने की आवश्यकता नहीं है SELECT ... FOR UPDATE। एक या दूसरे का उपयोग करें।


धन्यवाद क्रेग। आप सही थे- डेवलपर से गलती हुई थी। इस परीक्षण को चलाने के लिए धन्यवाद।
बेस्टप्रैक्टिस

SQL सर्वर के बारे में क्या? क्या लेन-देन अलगाव स्तर के स्वतंत्र रूप से अपडेट की गई पंक्ति पर हमेशा ताला लगा रहता है?
१०

@plalx खैर, प्रलेखन क्या कहता है? यदि आप इस तरह से इंटरेक्टिव टेस्ट चलाते हैं तो क्या होगा?
क्रेग रिंगर

@ क्रेगिंगर, यदि बी को ए से पहले ताला मिलता है लेकिन ए अपडेट के बाद क्या होगा?
मेंगट

1
@ मेनेंग यह नहीं है, इसलिए यह एक ताला है।
क्रेग रिंगर

0
UPDATE tbl SET owner = $me,
               id = LAST_INSERT_ID(id)
    WHERE owner = ''
    LIMIT 1;
$id = SELECT LAST_INSERT_ID();
Do some stuff (arbitrarily long time)...;
UPDATE  tbl SET owner = '' WHERE id = $id;

कोई लॉक नहीं (तालिका नहीं, लेन-देन नहीं), या यहां तक ​​कि वांछित:

  • UPDATE परमाणु है
  • LAST_INSERT_ID () सत्र-विशिष्ट है, इसलिए थ्रेड-सुरक्षित है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.