लॉक होने की कोशिश के दौरान मिले mysql के डेडलॉक से कैसे बचें; लेनदेन को पुनः आरंभ करने का प्रयास करें '


286

मेरे पास एक innoDB तालिका है जो ऑनलाइन उपयोगकर्ताओं को रिकॉर्ड करती है। यह उपयोगकर्ता द्वारा ताज़ा किए गए प्रत्येक पृष्ठ पर इस बात पर नज़र रखता है कि वे किस पृष्ठ पर हैं और साइट पर उनकी अंतिम पहुँच तिथि है। मेरे पास एक क्रोन है जो हर 15 मिनट में पुराने रिकॉर्ड को चलाता है।

मुझे एक 'डेडलॉक मिला जब ताला पाने की कोशिश कर रहा था; कल रात लगभग 5 मिनट के लिए लेनदेन को पुनः आरंभ करने का प्रयास करें और जब यह INSERTs को इस तालिका में चला रहा हो तो ऐसा प्रतीत होता है। क्या कोई सुझाव दे सकता है कि इस त्रुटि से कैसे बचा जाए?

=== EDIT ===

यहां वे प्रश्न हैं जो चल रहे हैं:

पहली साइट पर जाएँ:

INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3

प्रत्येक पृष्ठ पर ताज़ा करें:

UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888

क्रोन हर 15 मिनट:

DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

यह तब कुछ आँकड़े (यानी: सदस्यों ऑनलाइन, आगंतुकों ऑनलाइन) लॉग इन करने के लिए कुछ मायने रखता है।


क्या आप तालिका संरचना के बारे में कुछ और विवरण प्रदान कर सकते हैं? क्या कोई संकुल या गैर-अनुक्रमित अनुक्रमित हैं?
एंडर्स हाबिल

13
dev.mysql.com/doc/refman/5.1/en/innodb-deadlocks.html - रनिंग "शो इंजन इनोडब स्टेटस" उपयोगी डायग्नोस्टिक्स प्रदान करेगा।
मार्टिन

एक तुल्यकालिक डेटाबेस लिखने के लिए इसका अच्छा अभ्यास नहीं है जब उपयोगकर्ता किसी पृष्ठ पर कदम रखते हैं। इसे करने का सही तरीका यह है कि इसे स्मृति में रखें जैसे कि मेमचे या कुछ तेज़ कतार और क्रोन के साथ डीबी को लिखें।
Nir

जवाबों:


292

एक आसान चाल जो अधिकांश गतिरोधों के साथ मदद कर सकती है एक विशिष्ट क्रम में संचालन को छांट रही है।

आपको एक गतिरोध मिलता है जब दो लेनदेन विपरीत क्रम में दो ताले लगाने की कोशिश कर रहे हैं, अर्थात:

  • कनेक्शन 1: ताले कुंजी (1), ताले कुंजी (2);
  • कनेक्शन 2: ताले कुंजी (2), ताले कुंजी (1);

यदि दोनों एक ही समय पर चलते हैं, तो कनेक्शन 1 कुंजी (1) लॉक करेगा, कनेक्शन 2 लॉक कुंजी (2) लॉक करेगा और प्रत्येक कनेक्शन कुंजी -> गतिरोध जारी करने के लिए प्रतीक्षा करेगा।

अब, यदि आपने अपने प्रश्नों को बदल दिया है, तो कनेक्शन उसी क्रम पर कुंजियों को लॉक करेंगे, अर्थात:

  • कनेक्शन 1: ताले कुंजी (1), ताले कुंजी (2);
  • कनेक्शन 2: ताले कुंजी ( 1 ), ताले कुंजी ( 2 );

गतिरोध प्राप्त करना असंभव होगा।

तो यह मेरा सुझाव है:

  1. सुनिश्चित करें कि आपके पास कोई अन्य क्वेरी नहीं है जो डिलीट स्टेटमेंट को छोड़कर एक समय में एक से अधिक कुंजी को एक्सेस करता है। यदि आप करते हैं (और मुझे संदेह है कि आप करते हैं), आरोही क्रम में उनके (कहाँ k1, k2, .. kn) आदेश दें।

  2. आरोही क्रम में काम करने के लिए अपना डिलीट स्टेटमेंट ठीक करें:

परिवर्तन

DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

सेवा

DELETE FROM onlineusers WHERE id IN (SELECT id FROM onlineusers
    WHERE datetime <= now() - INTERVAL 900 SECOND order by id) u;

एक और बात ध्यान में रखना है कि mysql प्रलेखन का सुझाव है कि एक गतिरोध के मामले में ग्राहक को स्वचालित रूप से पुन: प्रयास करना चाहिए। आप इस तर्क को अपने ग्राहक कोड में जोड़ सकते हैं। (कहो, हार मानने से पहले इस विशेष त्रुटि पर 3 पीछे हट जाते हैं)।


2
अगर मेरे पास लेन-देन है (स्वतः पूर्ण = गलत), गतिरोध अपवाद। क्या केवल एक ही कथन को पुनः प्राप्त करना काफी है।
जिसका सेप 16'14

5
यदि आपके पास लेन-देन सक्षम है, तो यह सब या कुछ भी नहीं है। यदि आपके पास किसी भी प्रकार का अपवाद है, तो यह गारंटी है कि पूरे लेनदेन का कोई प्रभाव नहीं था। उस स्थिति में आप पूरी बात को फिर से शुरू करना चाहेंगे।
ओमि यदन

4
एक विशाल मेज पर एक चयन पर आधारित एक डिलीट एक साधारण डिलीट की तुलना में बहुत धीमा है
Thermech

3
बहुत बहुत धन्यवाद, यार। 'सॉर्ट स्टेटमेंट्स' टिप ने मेरे डेड लॉक मुद्दों को ठीक कर दिया।
मिरे

4
@OmryYadan जो मैं जानता हूं, MySQL में आप उसी तालिका से एक उप-वर्ग में नहीं चुन सकते हैं जिसमें आप अद्यतन कर रहे हैं। dev.mysql.com/doc/refman/5.7/en/update.html
artaxerxe

72

गतिरोध तब होता है जब दो लेनदेन लॉक प्राप्त करने के लिए एक-दूसरे पर इंतजार करते हैं। उदाहरण:

  • टीएक्स 1: लॉक ए, फिर बी
  • टीएक्स 2: लॉक बी, फिर ए

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

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


3
तो शायद मेरी समस्या यह है कि उपयोगकर्ता ने पृष्ठ को रीफ्रेश किया है और इस तरह एक रिकॉर्ड का एक अद्यतन ट्रिगर कर रहा है उसी समय क्रोन रिकॉर्ड पर एक DELETE चलाने की कोशिश कर रहा है। हालाँकि, INSERTS पर त्रुटि हो रही है, इसलिए क्रोन उन रिकॉर्डों को नष्ट नहीं करेगा जो अभी बनाए गए हैं। तो एक रिकॉर्ड पर गतिरोध कैसे हो सकता है जो अभी डाला जाना है?
डेविड

क्या आप तालिका के बारे में थोड़ी और जानकारी प्रदान कर सकते हैं और लेनदेन वास्तव में क्या करते हैं?
ewernli

मैं यह नहीं देखता कि प्रति लेनदेन केवल एक बयान होने पर गतिरोध कैसे हो सकता है। अन्य तालिकाओं पर कोई अन्य संचालन? कोई विशेष विदेशी कुंजी या अद्वितीय बाधाएं नहीं? कोई झरना बाधाओं को हटा नहीं?
ewernli

नहीं, और कुछ खास नहीं है ... मैं इसका उपयोग तालिका के उपयोग की प्रकृति के लिए करता हूं। आगंतुक से ताज़ा किए गए प्रत्येक पृष्ठ को एक पंक्ति सम्मिलित / अपडेट किया जा रहा है। किसी भी एक समय में लगभग 1000+ आगंतुक आते हैं।
डेविड

12

यह संभावना है कि डिलीट स्टेटमेंट तालिका में कुल पंक्तियों के एक बड़े हिस्से को प्रभावित करेगा। आखिरकार यह हटाने के लिए अधिग्रहित की जा रही एक टेबल लॉक हो सकती है। एक लॉक पर पकड़ (इस मामले में पंक्ति- या पेज लॉक) और अधिक लॉक प्राप्त करना हमेशा एक गतिरोध का जोखिम होता है। हालाँकि, मैं यह नहीं समझा सकता कि इन्सर्ट स्टेटमेंट लॉक एस्केलेशन की ओर क्यों जाता है - यह पेज स्प्लिटिंग / ऐडिंग के साथ करना पड़ सकता है, लेकिन MySQL को बेहतर तरीके से जानने वाले किसी व्यक्ति को इसमें भरना होगा।

एक शुरुआत के लिए यह स्पष्ट रूप से हटाने के बयान के लिए एक टेबल लॉक को स्पष्ट रूप से प्राप्त करने की कोशिश करने के लायक हो सकता है। देखें लॉक तालिकाएं और टेबल ताला मुद्दों


6

आप कोशिश कर सकते हैं कि deleteइस छद्मकोश की तरह एक अस्थायी तालिका में हटाए जाने वाली प्रत्येक पंक्ति की कुंजी को डालकर पहले काम संचालित हो।

create temporary table deletetemp (userid int);

insert into deletetemp (userid)
  select userid from onlineusers where datetime <= now - interval 900 second;

delete from onlineusers where userid in (select userid from deletetemp);

इसे इस तरह से तोड़ना कम कुशल है, लेकिन इसके दौरान की-रेंज लॉक रखने की आवश्यकता से बचा जाता है delete

साथ ही, 900 सेकंड से अधिक पुरानी पंक्तियों को छोड़कर selectएक whereखंड जोड़ने के लिए अपने प्रश्नों को संशोधित करें । यह क्रोन नौकरी पर निर्भरता से बचा जाता है और आपको इसे कम बार चलाने के लिए पुनर्निर्धारित करने की अनुमति देता है।

गतिरोध के बारे में सिद्धांत: मेरे पास MySQL में बहुत अधिक पृष्ठभूमि नहीं है, लेकिन यहां जाता है ... लेनदेन के बीच में deleteइसके whereखंड को जोड़े जाने से पंक्तियों को रोकने के लिए, डेटाइम के लिए एक कुंजी-श्रेणी लॉक रखने जा रहा है। , और जैसा कि इसे हटाने के लिए पंक्तियाँ मिलती हैं, यह संशोधित करने वाले प्रत्येक पृष्ठ पर एक लॉक प्राप्त करने का प्रयास करेगा। insertपेज यह में डालने पर एक ताला प्राप्त करने के लिए जा रहा है, और उसके बाद कुंजी लॉक प्राप्त करने का प्रयास। आम तौर पर insertउस कुंजी लॉक को खोलने के लिए धैर्यपूर्वक इंतजार करना होगा लेकिन यह गतिरोध होगा यदि deleteउसी पृष्ठ को लॉक करने का प्रयास करता है, insertक्योंकि deleteउस पृष्ठ का ताला और उस ताला की insertजरूरत को पूरा करता है। यह आवेषण के लिए सही नहीं लगता है, हालांकि deleteऔरinsert डेटाटाइम सीमाओं का उपयोग कर रहे हैं जो ओवरलैप नहीं करते हैं इसलिए शायद कुछ और चल रहा है।

http://dev.mysql.com/doc/refman/5.1/en/innodb-next-key-locking.html


4

मामले में कोई अभी भी इस मुद्दे से जूझ रहा है:

मुझे इसी तरह की समस्या का सामना करना पड़ा जहां 2 अनुरोध एक ही समय में सर्वर को मार रहे थे। नीचे जैसी कोई स्थिति नहीं थी:

T1:
    BEGIN TRANSACTION
    INSERT TABLE A
    INSERT TABLE B
    END TRANSACTION

T2:
    BEGIN TRANSACTION
    INSERT TABLE B
    INSERT TABLE A
    END TRANSACTION

इसलिए, मैं हैरान था कि गतिरोध क्यों हो रहा है।

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

इसका संदर्भ लें: https://blog.tekenlight.com/2019/02/21/database-deadlock-mysql.html


मेरे मामले में भी, ऐसा लगता है कि समस्या एक विदेशी संबंध थी। धन्यवाद 1
क्रिस प्रिंस

3

वसंत का उपयोग करने वाले जावा प्रोग्रामर के लिए, मैंने एक एओपी पहलू का उपयोग करके इस समस्या से बचा लिया है जो स्वचालित रूप से क्षणिक गतिरोध में चलने वाले लेनदेन को पुन: प्रयास करता है।

अधिक जानकारी के लिए @RetryTransaction Javadoc देखें ।


0

मेरे पास एक विधि है, जिसके आंतरिक भाग MySqlTransaction में लिपटे हुए हैं।

गतिरोध का मुद्दा मेरे लिए तब दिखा जब मैंने उसी पद्धति को समानांतर रूप से चलाया।

विधि का एक भी उदाहरण चलाने वाला कोई समस्या नहीं था।

जब मैंने MySqlTransaction को हटा दिया, तो मैं बिना किसी मुद्दे के साथ समानांतर रूप से विधि को चलाने में सक्षम था।

बस अपना अनुभव साझा कर रहा हूं, मैं किसी चीज की वकालत नहीं कर रहा हूं।


0

cronखतरनाक है। यदि क्रोन का एक उदाहरण अगले होने से पहले समाप्त होने में विफल रहता है, तो वे एक दूसरे से लड़ने की संभावना रखते हैं।

लगातार चलने वाली नौकरी करना बेहतर होगा जो कुछ पंक्तियों को हटा देगा, कुछ को सोएगा, फिर दोहराएगा।

इसके अलावा, INDEX(datetime)गतिरोध से बचने के लिए बहुत महत्वपूर्ण है।

लेकिन, यदि डेटटाइम टेस्ट में 20% से अधिक शामिल हैं, DELETEतो तालिका का स्कैन करेगा। अधिक बार हटाए गए छोटे टुकड़े एक वैकल्पिक हल है।

छोटे विखंडू के साथ जाने का एक अन्य कारण कम पंक्तियों को लॉक करना है।

जमीनी स्तर:

  • INDEX(datetime)
  • लगातार चलने वाला कार्य - हटाना, एक मिनट सोना, दोहराना।
  • यह सुनिश्चित करने के लिए कि उपरोक्त कार्य समाप्त नहीं हुआ है, एक क्रॉन नौकरी है जिसका एकमात्र उद्देश्य विफलता पर इसे फिर से शुरू करना है।

अन्य विलोपन तकनीक: http://mysql.rjweb.org/doc.php/deletebig

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