multithreading परमाणु अद्यतन.. पोस्टग्रेज़ में चुनें




postgresql concurrency (2)

जबकि इरविन का सुझाव संभवतः सही व्यवहार पाने का सबसे आसान तरीका है (जब तक आप 40001 के SQLSTATE के साथ अपवाद प्राप्त करते हैं तो अपने लेनदेन को फिर से प्रयास करें), उनके प्रकृति द्वारा कतारबद्ध अनुप्रयोगों को उनकी बारी लेने का मौका देने के अनुरोधों के साथ बेहतर काम करना पड़ता है SERIALIZABLE लेनदेन के PostgreSQL कार्यान्वयन के मुकाबले कतार में, जो उच्च SERIALIZABLE अनुमति देता है और टकराव की संभावनाओं के बारे में कुछ और "आशावादी" है।

प्रश्न में उदाहरण क्वेरी, जैसा कि यह खड़ा है, डिफ़ॉल्ट READ COMMITTED लेनदेन अलगाव स्तर में कतार से एक ही पंक्ति "दावा" दोनों के लिए दो (या अधिक) समवर्ती कनेक्शन की अनुमति होगी। यह क्या होगा:

  • टी 1 शुरू होता है और UPDATE चरण में पंक्ति को लॉक करने तक जाता है।
  • टी 2 निष्पादन समय में टी 1 ओवरलैप करता है और उस पंक्ति को अद्यतन करने का प्रयास करता है। यह टी 1 के COMMIT या ROLLBACK लंबित ब्लॉक करता है।
  • टी 1 सफलतापूर्वक "दावा" पंक्ति के साथ काम करता है।
  • टी 2 पंक्ति को अद्यतन करने का प्रयास करता है, यह पता चलता है कि टी 1 में पहले से ही, पंक्ति के नए संस्करण की तलाश है, यह पता चलता है कि यह अभी भी चयन मानदंड (जो कि id ) को संतुष्ट करता है, और पंक्ति का भी दावा करता है।

इसे सही तरीके से काम करने के लिए संशोधित किया जा सकता है (यदि आप PostgreSQL के संस्करण का उपयोग कर रहे हैं जो उपरोक्त में FOR UPDATE खंड की अनुमति देता है)। बस उपरोक्त के अंत में FOR UPDATE लिए जोड़ें जो आईडी का चयन करता है, और यह होगा:

  • टी 1 शुरू होता है और अब आईडी चुनने से पहले पंक्ति को ताला लगा देता है।
  • टी 2 निष्पादन समय और ब्लॉक में आईडी को चुनने का प्रयास करते समय टी 1 ओवरलैप करता है, COMMIT के COMMIT या ROLLBACK लंबित है।
  • टी 1 सफलतापूर्वक "दावा" पंक्ति के साथ काम करता है।
  • जब तक टी 2 आईडी देखने के लिए पंक्ति को पढ़ने में सक्षम होता है, तो यह देखता है कि इसका दावा किया गया है, इसलिए यह अगली उपलब्ध आईडी पाता है।

SERIALIZABLE REPEATABLE READ या SERIALIZABLE लेनदेन अलगाव स्तर पर, लेखन संघर्ष एक त्रुटि फेंक देगा, जिसे आप पकड़ सकते हैं और निर्धारित कर सकते हैं कि SQLSTATE के आधार पर एक क्रमिक विफलता थी, और पुनः प्रयास करें।

यदि आप आम तौर पर सर्जिकल लेनदेन चाहते हैं लेकिन आप क्यूइंग क्षेत्र में रीट्रीज़ से बचना चाहते हैं, तो आप सलाहकार लॉक का उपयोग करके इसे पूरा करने में सक्षम हो सकते हैं।

मैं एक तरह के क्यूइंग तंत्र का निर्माण कर रहा हूँ। डेटा की पंक्तियां हैं जिन्हें प्रसंस्करण की आवश्यकता होती है, और स्थिति ध्वज। मैं एक update .. returning का उपयोग कर रहा हूँ update .. returning इसे प्रबंधित करने के लिए खंड update .. returning है:

UPDATE stuff
SET computed = 'working'
WHERE id = (SELECT id from STUFF WHERE computed IS NULL LIMIT 1)
RETURNING * 

क्या नेस्टेड चुनिंदा हिस्सा अपडेट के समान लॉक है, या क्या मेरे पास दौड़ की स्थिति है? यदि हां, तो आंतरिक चयन को select for update करने की आवश्यकता है?


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

हालांकि, एक लेनदेन कभी भी अपने साथ संघर्ष नहीं करता है।

समवर्ती उपयोग के लिए , मामला अधिक जटिल हो सकता है। आप SERIALIZABLE लेनदेन मोड के साथ सुरक्षित पक्ष पर SERIALIZABLE :

BEGIN ISOLATION LEVEL SERIALIZABLE;
UPDATE stuff
SET    computed = 'working'
WHERE  id = (SELECT id FROM stuff WHERE computed IS NULL LIMIT 1)
RETURNING * 
COMMIT;

आपको क्रमबद्ध विफलताओं के लिए तैयार करने की आवश्यकता है और इस तरह के मामले में अपनी क्वेरी को पुनः प्रयास करें।

लेकिन मुझे पूरा यकीन नहीं है कि यह अधिक नहीं है। मैं @ kgrittn से रोकने के लिए कहूंगा .. वह समवर्ती और serializable लेनदेन के साथ विशेषज्ञ है ..

और उसने किया। :)

दोनों ओर से लाभदायक

डिफ़ॉल्ट लेनदेन मोड में क्वेरी चलाएं READ COMMITTED

पोस्टग्रेस 9.5 के लिए या बाद में FOR UPDATE SKIP LOCKED उपयोग करें। देख:

पुराने संस्करणों के लिए बाहरी UPDATE में स्पष्ट रूप से computed IS NULL स्थिति को computed IS NULL UPDATE :

UPDATE stuff
SET    computed = 'working'
WHERE  id = (SELECT id FROM stuff WHERE computed IS NULL LIMIT 1)
AND   computed IS NULL;

जैसा कि @ kgrittn ने अपने उत्तर में टिप्पणी में सलाह दी है, यह प्रश्न कुछ भी किए बिना खाली हो सकता है, (संभावना नहीं) मामले में यह एक समवर्ती लेनदेन के साथ अंतर्निहित हो गया है।

इसलिए, यह लेन-देन मोड SERIALIZABLE में पहले संस्करण की तरह काम करेगा, आपको फिर से प्रदर्शन करना होगा - प्रदर्शन प्रदर्शन के बिना।

एकमात्र समस्या: जबकि संघर्ष बहुत ही असंभव है क्योंकि अवसर की खिड़की बहुत छोटी है, यह भारी भार के तहत हो सकती है। आप यह सुनिश्चित करने के लिए नहीं कह सकते कि आखिर में कोई और पंक्ति नहीं छोड़ी गई है या नहीं।

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

LOOP
   UPDATE stuff
   SET    computed = 'working'
   WHERE  id = (SELECT id FROM stuff WHERE computed IS NULL
                LIMIT 1 FOR UPDATE SKIP LOCKED);  -- pg 9.5+
   -- WHERE  id = (SELECT id FROM stuff WHERE computed IS NULL LIMIT 1)
   -- AND    computed IS NULL; -- pg 9.4-

   CONTINUE WHEN FOUND;  -- continue outside loop, may be a nested loop

   UPDATE stuff
   SET    computed = 'working'
   WHERE  id = (SELECT id FROM stuff WHERE computed IS NULL
                LIMIT 1 FOR UPDATE);

   EXIT WHEN NOT FOUND;  -- exit function (end)
END LOOP;

आपको दोनों दुनिया के सर्वश्रेष्ठ प्रदर्शन करना चाहिए: प्रदर्शन और विश्वसनीयता।







transaction-isolation