InnoDB पंक्ति लॉकिंग - कैसे लागू करें


13

मैं अब चारों ओर देख रहा हूं, mysql साइट को पढ़ना और मैं अभी भी यह नहीं देख सकता कि यह कैसे काम करता है।

मैं लेखन के लिए परिणाम का चयन और पंक्ति लॉक करना चाहता हूं, परिवर्तन लिखें और लॉक जारी करें। ऑडोकोमाइट चालू है।

योजना

id (int)
name (varchar50)
status (enum 'pending', 'working', 'complete')
created (datetime)
updated (datetime) 

लंबित स्थिति के साथ एक आइटम का चयन करें, और इसे काम करने के लिए अद्यतन करें। यह सुनिश्चित करने के लिए एक विशेष लेखन का उपयोग करें कि एक ही आइटम दो बार नहीं उठाया गया है।

इसलिए;

"SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR WRITE"

परिणाम से आईडी प्राप्त करें

"UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>

क्या मुझे लॉक जारी करने के लिए कुछ भी करने की ज़रूरत है, और क्या यह काम करता है जैसे मैंने ऊपर किया है?

जवाबों:


26

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

यहां सकीला डेटाबेस से एक उदाहरण सत्र है जो कुछ अद्यतन प्रश्नों के व्यवहार को प्रदर्शित करता है।

सबसे पहले, बस इतना ही कि हम क्रिस्टल स्पष्ट हैं, लेन-देन अलगाव स्तर को READATABLE READ में सेट करें। यह सामान्य रूप से अनावश्यक है, क्योंकि यह InnoDB के लिए डिफ़ॉल्ट अलगाव स्तर है:

session1> SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
session1> BEGIN;
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA      | WILLIAMS  |
+------------+-----------+
1 row in set (0.00 sec)    

अन्य सत्र में, इस पंक्ति को अपडेट करें। लिंडा ने शादी की और अपना नाम बदल लिया:

session2> UPDATE customer SET last_name = 'BROWN' WHERE customer_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

सत्र 1 में वापस, क्योंकि हम दोहराए गए विवरण में थे, लिंडा अभी भी लिंडा विलियम्स है:

session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA      | WILLIAMS  |
+------------+-----------+
1 row in set (0.00 sec)

लेकिन अब, हम इस पंक्ति के लिए अनन्य पहुँच चाहते हैं, इसलिए हम पंक्ति पर अद्यतन के लिए कॉल करते हैं। ध्यान दें कि अब हमें पंक्ति के सबसे हाल के संस्करण मिलते हैं, जिसे इस लेनदेन के बाहर सत्र 2 में अपडेट किया गया था। यह दोहराई गई खबर नहीं है, यह पढ़ी गई टिप्पणी है

session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3 FOR UPDATE;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA      | BROWN     |
+------------+-----------+
1 row in set (0.00 sec)

सत्र 1 में लॉक सेट का परीक्षण करते हैं। ध्यान दें कि session2 पंक्ति को अपडेट नहीं कर सकता है।

session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

लेकिन हम अभी भी इससे सेलेक्ट कर सकते हैं

session2> SELECT c.customer_id, c.first_name, c.last_name, a.address_id, a.address FROM customer c JOIN address a USING (address_id) WHERE c.customer_id = 3;
+-------------+------------+-----------+------------+-------------------+
| customer_id | first_name | last_name | address_id | address           |
+-------------+------------+-----------+------------+-------------------+
|           3 | LINDA      | BROWN     |          7 | 692 Joliet Street |
+-------------+------------+-----------+------------+-------------------+
1 row in set (0.00 sec)

और हम अभी भी एक विदेशी कुंजी संबंध के साथ एक चाइल्ड टेबल को अपडेट कर सकते हैं

session2> UPDATE address SET address = '5 Main Street' WHERE address_id = 7;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

session1> COMMIT;

एक और दुष्प्रभाव यह है कि आप गतिरोध पैदा करने की संभावना को बहुत बढ़ा देते हैं।

अपने विशिष्ट मामले में, आप शायद चाहते हैं:

BEGIN;
SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR UPDATE;
-- do some other stuff
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>;
COMMIT;

यदि "कुछ अन्य सामान करते हैं" टुकड़ा अनावश्यक है और आपको वास्तव में पंक्ति के बारे में जानकारी रखने की आवश्यकता नहीं है, तो चयन के लिए अद्यतन अनावश्यक और बेकार है और आप इसके बजाय बस एक अद्यतन चला सकते हैं:

UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;

आशा है कि यह कुछ समझ में आता है।


3
धन्यवाद। यह मेरे मुद्दे को हल करने के लिए प्रतीत नहीं होता है, जब दो धागे "सिलेक्ट आईडी फ्रॉम itemsव्हेयर status= 'लंबित' लिमिट 1 फॉर अपडेट के साथ आ रहे हैं ;" और वे दोनों एक ही पंक्ति को देखते हैं, तो एक दूसरे को बंद कर देगा। मैं किसी तरह उम्मीद कर रहा था कि यह लॉक की गई पंक्ति को पास-पास करने में सक्षम हो जाएगा और इसके साथ ही अगला आइटम भी लंबित हो जाएगा ..
Wizzard

1
डेटाबेस की प्रकृति यह है कि वे लगातार डेटा लौटाते हैं। यदि आप मूल्य को अपडेट किए जाने से पहले दो बार उस क्वेरी को निष्पादित करते हैं, तो आपको वही परिणाम वापस मिलेगा। कोई भी "मुझे इस क्वेरी से मेल खाने वाला पहला मूल्य नहीं मिलता है, जब तक कि पंक्ति लॉक नहीं होती है" एसक्यूएल एक्सटेंशन जो मुझे पता है। यह संदेह की तरह लगता है जैसे आप एक रिलेशनल डेटाबेस के शीर्ष पर एक कतार को लागू कर रहे हैं। क्या यह मामला है?
आरोन ब्राउन

हारून; हां, यही मैं करने की कोशिश कर रहा हूं। मैंने गियरमैन की तरह कुछ का उपयोग करते हुए देखा है - लेकिन यह एक हलचल थी। आपके मन में कुछ और है?
Wizzard

मुझे लगता है कि आपको इसे पढ़ना चाहिए: engineyard.com/blog/2011/… - संदेश कतारों के लिए, आपकी पसंद की ग्राहक भाषा के आधार पर उनमें से बहुत सारे हैं। ActiveMQ, Resque (रूबी + रेडिस), ZeroMQ, RabbitMQ, आदि
आरोन ब्राउन

मैं इसे कैसे बना सकता हूं ताकि सत्र 1 में अद्यतन होने तक सत्र 2 ब्लॉक पढ़ने पर हो?
CMCDragonkai

2

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

विभिन्न ग्राहक एक ही पंक्तियों को एक साथ पढ़ सकते हैं।

विभिन्न ग्राहक अलग-अलग पंक्तियों को एक साथ संशोधित कर सकते हैं।

विभिन्न ग्राहक एक ही समय में एक ही पंक्ति को संशोधित नहीं कर सकते हैं। यदि कोई लेन-देन एक पंक्ति को संशोधित करता है, तो अन्य लेनदेन पहली पंक्ति पूरी होने तक उसी पंक्ति को संशोधित नहीं कर सकते हैं। अन्य लेन-देन संशोधित पंक्ति नहीं पढ़ सकते हैं, या तो, जब तक कि वे READ UNCOMMITTED अलगाव स्तर का उपयोग नहीं कर रहे हों। यही है, वे मूल असंबद्ध पंक्ति देखेंगे।

मूल रूप से, आपको स्पष्ट लॉकिंग निर्दिष्ट करने की आवश्यकता नहीं है InnoDB इसे इटसेल्फ हैंडल करता है हालांकि कुछ स्थिति में आपको स्पष्ट लॉक के बारे में स्पष्ट विवरण देना पड़ सकता है:

निम्न सूची उपलब्ध लॉक प्रकारों और उनके प्रभावों का वर्णन करती है:

पढ़ें

पढ़ने के लिए एक मेज को बंद कर देता है। READ लॉक पढ़ने वाले प्रश्नों के लिए एक तालिका को लॉक करता है जैसे SELECT जो तालिका से डेटा पुनर्प्राप्त करता है। यह INSERT, DELETE, या UPDATE जैसे ऑपरेशन को लिखने की अनुमति नहीं देता है, यहां तक ​​कि लॉक को रखने वाले क्लाइंट द्वारा टेबल को संशोधित भी करते हैं। जब एक तालिका पढ़ने के लिए बंद कर दी जाती है, तो अन्य ग्राहक उसी समय तालिका से पढ़ सकते हैं, लेकिन कोई भी ग्राहक इसे नहीं लिख सकता है। एक क्लाइंट जो पढ़ने के लिए एक टेबल पर लिखना चाहता है, उसे तब तक इंतजार करना चाहिए जब तक कि वर्तमान में उसके द्वारा पढ़े गए सभी क्लाइंट्स ने अपना लॉक खत्म नहीं कर दिया हो।

लिखना

लिखने के लिए एक मेज को बंद कर देता है। WRITE लॉक एक विशेष लॉक है। यह केवल तभी प्राप्त किया जा सकता है जब किसी तालिका का उपयोग नहीं किया जा रहा हो। एक बार अधिग्रहित होने के बाद, केवल लिखने का ताला रखने वाला ग्राहक टेबल से पढ़ या लिख ​​सकता है। अन्य ग्राहक न तो इससे पढ़ सकते हैं और न ही इसे लिख सकते हैं। कोई अन्य क्लाइंट पढ़ने या लिखने के लिए टेबल को लॉक नहीं कर सकता है।

पढ़ें LOCAL

पढ़ने के लिए एक मेज को बंद कर देता है, लेकिन समवर्ती आवेषण की अनुमति देता है। समवर्ती आवेषण "पाठकों ब्लॉक लेखकों" सिद्धांत का एक अपवाद है। यह केवल MyISAM तालिकाओं पर लागू होता है। यदि किसी MyISAM तालिका में हटाए गए या अपडेट किए गए रिकॉर्डों के परिणामस्वरूप बीच में कोई छेद नहीं है, तो आवेषण हमेशा तालिका के अंत में होते हैं। उस स्थिति में, एक क्लाइंट जो तालिका से पढ़ रहा है, वह अन्य ग्राहकों को तालिका में सम्मिलित करने की अनुमति देने के लिए एक READ LOCAL लॉक के साथ लॉक कर सकता है जबकि रीड लॉक रखने वाला क्लाइंट इससे पढ़ता है। यदि किसी MyISAM तालिका में छेद हैं, तो आप तालिका को डीफ़्रैग्मेन्ट करने के लिए OPTIMIZE TABLE का उपयोग करके उन्हें निकाल सकते हैं।


जवाब के लिए धन्यवाद। जैसा कि मेरे पास यह तालिका है और 100 ग्राहक लंबित वस्तुओं की जांच कर रहे हैं, मुझे बहुत सी टक्कर मिल रही थीं - 2-3 ग्राहकों को एक ही लंबित पंक्ति मिल रही थी। टेबल लॉक को धीमा करना है।
विज्ज़र्ड

0

एक अन्य विकल्प एक कॉलम जोड़ना होगा जो अंतिम सफल लॉक का समय संग्रहीत करता है और फिर कुछ और जो पंक्ति को लॉक करना चाहता था, उसे तब तक इंतजार करना होगा जब तक कि या तो मंजूरी दे दी गई या 5 मिनट (या जो भी) बीत चुके थे।

कुछ इस तरह...

Schema

id (int)
name (varchar50)
status (enum 'pending', 'working', 'complete')
created (datetime)
updated (datetime)
lastlock (int)

लास्टलॉक एक int है क्योंकि यह यूनिक्स टाइमस्टैम्प को अपनी तुलना में आसान (और शायद जल्दी) के रूप में संग्रहीत करता है।

// शब्दार्थवाद का बहाना करके, मैंने उन्हें पूरी तरह से चलाने की जाँच नहीं की है, लेकिन यदि वे नहीं करते हैं तो उन्हें पर्याप्त पास होना चाहिए।

UPDATE items 
  SET lastlock = UNIX_TIMESTAMP() 
WHERE 
  lastlock = 0
  OR (UNIX_TIMESTAMP() - lastlock) > 360;

फिर यह देखने के लिए कि कितनी पंक्तियों को अपडेट किया गया था, क्योंकि पंक्तियों को एक साथ दो प्रक्रियाओं से अपडेट नहीं किया जा सकता है, यदि आपने पंक्ति को अपडेट किया है, तो आपको लॉक मिला है। मान लें कि आप PHP का उपयोग कर रहे हैं, तो आप mysql_affected_rows () का उपयोग करेंगे, यदि उस से वापसी 1 थी, तो आपने इसे सफलतापूर्वक स्वीकार कर लिया।

फिर आप या तो लास्टलॉक को 0 पर अपडेट कर सकते हैं इसके बाद आपको जो करना है, या आलसी होना चाहिए और 5 मिनट प्रतीक्षा करें जब अगले लॉक का प्रयास वैसे भी सफल होगा।

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


-1

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

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

इस प्रकार, कई एकीकृत रीड रिकॉर्ड के अनुसार एकीकृत रीड फ़ील्ड प्रति अंश लिखते हैं। ये संख्यात्मक टुकड़े भी खुद को ECC, एन्क्रिप्शन और ब्लॉक स्तर हस्तांतरण / भंडारण के लिए उधार देते हैं। जितने अधिक लेखन के टुकड़े होंगे, संतृप्त डेटा पर समानांतर / समवर्ती लेखन पहुंच की गति उतनी ही अधिक होगी।

इस मुद्दे के साथ MMORPG व्यापक रूप से पीड़ित होते हैं, जब बड़ी संख्या में खिलाड़ी सभी एक दूसरे को प्रभाव कौशल के क्षेत्र से मारने लगते हैं। उन कई खिलाड़ियों को सभी को हर दूसरे खिलाड़ी को लिखने / अपडेट करने की आवश्यकता होती है, ठीक उसी समय, समानांतर में, अनकैप्ड प्लेयर रिकॉर्ड पर राइटिंग लॉकिंग स्टॉर्म बनाते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.