UPDATE / INSERT संयोजन के लिए Postgres में लॉकिंग


11

मेरे पास दो टेबल हैं। एक लॉग टेबल है; एक अन्य में, अनिवार्य रूप से, कूपन कोड हैं जो केवल एक बार उपयोग किए जा सकते हैं।

उपयोगकर्ता को एक कूपन को भुनाने में सक्षम होना चाहिए, जो लॉग टेबल में एक पंक्ति सम्मिलित करेगा और उपयोग के रूप में ( usedस्तंभ को अपडेट करके true) कूपन को चिह्नित करेगा ।

स्वाभाविक रूप से, यहां एक स्पष्ट दौड़ की स्थिति / सुरक्षा मुद्दा है।

मैंने MySQL की दुनिया में अतीत में ऐसी ही चीजें की हैं। उस दुनिया में, मैं विश्व स्तर पर दोनों तालिकाओं को बंद कर दूंगा, तर्क को इस ज्ञान में सुरक्षित कर दूंगा कि यह केवल एक बार हो सकता है, और फिर एक बार जब मैं किया गया तो तालिकाओं को अनलॉक कर दूंगा।

क्या Postgres में ऐसा करने का एक बेहतर तरीका है? विशेष रूप से, मुझे चिंता है कि ताला वैश्विक है, लेकिन होना नहीं है - मुझे वास्तव में केवल यह सुनिश्चित करने की आवश्यकता है कि कोई और उस विशेष कोड में प्रवेश करने की कोशिश नहीं कर रहा है, इसलिए शायद कुछ पंक्ति-स्तरीय लॉकिंग काम करेगा?

जवाबों:


15

मैंने इससे पहले MySQL में समसामयिक समस्याओं के बारे में सुना है। पोस्टग्रेज में ऐसा नहीं है।

डिफ़ॉल्ट READ COMMITTEDलेन-देन अलगाव स्तर में निर्मित पंक्ति-स्तरीय लॉक पर्याप्त हैं।

मैं एक डेटा-संशोधित CTE (कुछ ऐसा है जो MySQL भी नहीं है) के साथ एक ही कथन का सुझाव देता हूं क्योंकि यह एक तालिका से दूसरी तालिका में मानों को पास करने के लिए सुविधाजनक है (यदि आपको इसकी आवश्यकता होनी चाहिए)। यदि आपको couponटेबल से किसी चीज की जरूरत नहीं है तो आप अलग से UPDATEऔर INSERTस्टेटमेंट के साथ भी लेन-देन का उपयोग कर सकते हैं ।

WITH upd AS (
   UPDATE coupon
   SET    used = true
   WHERE  coupon_id = 123
   AND    NOT used
   RETURNING coupon_id, other_column
   )
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;

यह एक दुर्लभ बात होनी चाहिए कि एक से अधिक लेनदेन एक ही कूपन को भुनाने की कोशिश करते हैं। उनके पास एक अद्वितीय संख्या है, है न? एक ही समय में एक से अधिक लेन-देन की कोशिश करना बहुत दुर्लभ होना चाहिए, फिर भी। (हो सकता है कि कोई एप्लिकेशन बग या सिस्टम को चलाने के लिए कोई व्यक्ति कोशिश कर रहा हो?)

जैसा कि यह हो सकता है, UPDATEकेवल एक ही लेनदेन के लिए सफल होता है , चाहे कोई भी हो। एक UPDATEएक का अधिग्रहण पंक्ति स्तर ताला प्रत्येक लक्ष्य पंक्ति को अद्यतन करने से पहले पर। यदि एक समवर्ती लेन-देन एक UPDATEही पंक्ति में करने की कोशिश करता है , तो यह पंक्ति पर लॉक को देखेगा और तब तक प्रतीक्षा करेगा जब तक कि अवरुद्ध लेनदेन समाप्त नहीं हो जाता ( ROLLBACKया COMMIT), फिर लॉक कतार में पहला होना:

  • यदि प्रतिबद्ध है, तो स्थिति को फिर से जाँचें। यदि यह अभी भी है NOT used, तो पंक्ति को लॉक करें और आगे बढ़ें। वरना UPDATEअब कोई योग्यता पंक्ति पाता है और करता कुछ भी नहीं कोई पंक्ति लौट रहा है, इसलिए, INSERTयह भी कुछ नहीं करता है।

  • यदि वापस लुढ़का हुआ है, तो पंक्ति को लॉक करें और आगे बढ़ें।

नहीं है एक दौड़ हालत के लिए कोई संभावित

नहीं है एक के लिए कोई संभावित गतिरोध जब तक आप एक ही लेन-देन में और अधिक लेखन डाल या अन्यथा सिर्फ एक से अधिक पंक्तियों ताला।

INSERTध्यान-मुक्त है। यदि, कुछ गलती से coupon_idपहले से ही logतालिका में है (और आपके पास एक UNIQUE या PK बाधा है log.coupon_id), तो पूरे लेनदेन को एक अद्वितीय उल्लंघन के बाद वापस कर दिया जाएगा। आपके DB में एक अवैध राज्य का संकेत देगा। यदि उपरोक्त कथन logतालिका में लिखने का एकमात्र तरीका है , तो ऐसा कभी नहीं होना चाहिए।


यह वास्तव में एक दुर्लभ बात होनी चाहिए कि एक से अधिक लेनदेन एक ही कोड को भुनाने की कोशिश करते हैं, लेकिन आपका संदेह सही है कि यह विशेष रूप से तब होगा जब कोई सिस्टम को गेम करने की कोशिश कर रहा हो। इसके लिए बहुत बहुत धन्यवाद - पोस्टग्रेज में जाने के लिए सीटीई मेरे लिए एक बड़ा ड्रॉ था, लेकिन मुझे यह महसूस नहीं हुआ कि इसके लिए निहित लॉकिंग काफी अच्छा होगा।
रॉब मिलर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.