मॉड्यूल पाठ (3/4)
लॉक करें और अपडेट के लिए चुनें
यह समझना कि एक डेटाबेस एक टर्मिनल पर अलगाव में कैसे काम करता है, सरल है... रुको नहीं, मैंने पहले ही यह कहा था!
कभी-कभी "परमाणु अद्यतन" (उदा. stock = stock - 1) पर्याप्त नहीं होते हैं।
आइए कल्पना करें कि आपको यह जांचना है कि उपयोगकर्ता के पास 50€ मूल्य का सामान खरीदने के लिए पर्याप्त बैलेंस है या नहीं:
SELECT balance FROM users WHERE id = 1;- जेएस: यदि शेष < 50 है, तो उन्हें ब्लॉक करें!
- अन्यथा चेकआउट के साथ आगे बढ़ें...
- कटौती चलाएँ! (और यहां आपको एक उपयोगकर्ता मिलता है, जिसने चरण 2 पर चेक को सत्यापित करते समय, एक और समानांतर चेक निकाल दिया, जो उनके खाते से जल गया और अब आप शून्य से नीचे जा रहे हैं!)।
अद्यतन स्पष्ट लॉकिंग के लिए
SELECT के अंत में FOR UPDATE जोड़ने से जादुई तरीके से आपकी निर्दोष रीडिंग अगले कमिट तक अन्य ग्राहकों के खिलाफ एक सीलबंद लॉक में बदल जाती है। जब तक आप यह तय नहीं कर लेते कि उनके साथ क्या करना है, कोई भी उन विशिष्ट चयनित पंक्तियों पर अद्यतन (या समवर्ती रीड-फॉर-अपडेट) करने में सक्षम नहीं होगा!
BEGIN;
-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;
-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic
UPDATE users SET balance = balance - 50 WHERE id = 3;
COMMIT;यहां तक कि FOR SHARE भी है जो अन्य पाठकों को स्टेट रीड को ब्लॉक किए बिना (यदि बेस लॉक पर है) पढ़ना जारी रखने देता है, लेकिन आपके प्रतिबद्ध होने तक किसी भी संभावित बाद के अपडेट को ब्लॉक कर देता है। हालाँकि संशोधनों के लिए, हमेशा विशेष लॉकिंग क्लॉज की क्रूर शक्ति का उपयोग करें:
खुद कोशिश करना
एक सामान्य समय-सीमा वाला लेन-देन खोलें और विशेष रूप से उत्पाद आईडी=5 की रीडिंग को लॉक करें।\nदो-चरणीय क्वेरी चलाएँ:\n1. लेन-देन खोलें (शुरू करें)\n2. 'अद्यतन के लिए' विकल्प संलग्न करके, क्वेरी 'उत्पाद' आईडी 5 के लिए पूरी पंक्ति इस गारंटी के साथ लाती है कि कोई भी इसे बदल नहीं पाएगा।
संकेत दिखाएँ
प्रारंभ करें;, नई पंक्ति, उन उत्पादों से चुनें * जहां अपडेट के लिए आईडी = 5 है;
3 प्रयासों के बाद समाधान उपलब्ध है
कभी-कभी लेन-देन विनाशकारी अवधियों (गतिरोध या अनंत प्रतीक्षा) के लिए रुक जाता है।\nइसे रोकने के लिए आप डेटाबेस को उस क्षण को छोड़ने का आदेश दे सकते हैं जब पंक्ति तुरंत उपलब्ध नहीं होती है: अंत में NOWAIT का उपयोग करें, जिससे SQL सर्वर अपवाद हमेशा के लिए लटके बिना जेएस में वापस आ जाएगा।\nश्रेणी ID=1 के लिए SELECT चलाएँ और FOR UPDATE का उपयोग करें लेकिन NOWAIT को लागू करें।
संकेत दिखाएँ
उन श्रेणियों में से चुनें * जहां आईडी = 1 अभी अपडेट के लिए है;
3 प्रयासों के बाद समाधान उपलब्ध है