2010-04-07 11 views
25

मेरे पास लगभग 5,000,000 पंक्तियों वाली एक MySQL तालिका है जिसे डीबीआई के माध्यम से समानांतर पर्ल प्रक्रियाओं द्वारा लगातार छोटे तरीकों से अद्यतन किया जा रहा है। तालिका में लगभग 10 कॉलम और कई इंडेक्स हैं।MySQL त्रुटि के आसपास काम करना "लॉक प्राप्त करने का प्रयास करते समय पाया गया डेडलॉक; लेनदेन को पुनरारंभ करने का प्रयास करें"

एक बहुत ही आम आपरेशन निम्न त्रुटि कभी कभी को जन्म देता है:

UPDATE file_table SET a_lock = 'process-1234' WHERE param1 = 'X' AND param2 = 'Y' AND param3 = 'Z' LIMIT 47 

त्रुटि केवल कभी कभी शुरू हो रहा है:

DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction at Db.pm line 276. 

एसक्यूएल बयान है कि त्रुटि से चलाता है कुछ इस तरह है। मैं 1% कॉल या उससे कम अनुमान लगाऊंगा। हालांकि, यह एक छोटी सी मेज के साथ कभी नहीं हुआ और डेटाबेस बढ़ने के साथ ही अधिक आम हो गया है।

ध्यान दें कि मैं file_table में a_lock फ़ील्ड का उपयोग कर रहा हूं यह सुनिश्चित करने के लिए कि चार निकट-समान प्रक्रियाएं जो मैं चल रहा हूं, उसी पंक्ति पर कोशिश करने और काम करने की कोशिश नहीं करते हैं। सीमा को उनके काम को छोटे हिस्सों में तोड़ने के लिए डिज़ाइन किया गया है।

मैंने MySQL या DBD :: mysql पर बहुत अधिक ट्यूनिंग नहीं की है। MySQL एक मानक सोलारिस तैनाती है, और डेटाबेस कनेक्शन की स्थापना की है इस प्रकार है:

my $dsn = "DBI:mysql:database=" . $DbConfig::database . ";host=${DbConfig::hostname};port=${DbConfig::port}"; 
my $dbh = DBI->connect($dsn, $DbConfig::username, $DbConfig::password, { RaiseError => 1, AutoCommit => 1 }) or die $DBI::errstr; 

मैं ऑनलाइन देखा है कि कई अन्य लोगों को मिलती-जुलती त्रुटियां सूचित किया है और यह एक वास्तविक गतिरोध स्थिति हो सकती है।

मैं दो प्रश्न हैं:

  1. वास्तव में क्या बारे में मेरी स्थिति उपरोक्त त्रुटि उत्पन्न कर रहा है?

  2. क्या इसके आसपास काम करने या इसकी आवृत्ति को कम करने का कोई आसान तरीका है? उदाहरण के लिए, मैं "डीबीपीएम लाइन 276 पर लेनदेन को पुनरारंभ करने" के बारे में वास्तव में कैसे जा सकता हूं?

अग्रिम धन्यवाद।

उत्तर

61

आप InnoDB या किसी पंक्ति-स्तर व्यवहार RDBMS का उपयोग कर रहे हैं, तो यह संभव है कि किसी भी लिखने लेन-देन एक गतिरोध पैदा कर सकता है, यहां तक ​​कि पूरी तरह से सामान्य स्थितियों में। बड़ी टेबल, बड़े लिखते हैं, और लंबे लेनदेन ब्लॉक अक्सर होने वाले डेडलॉक्स की संभावना में वृद्धि करेंगे। आपकी स्थिति में, यह शायद इनके संयोजन का है।

डेडलॉक्स को वास्तव में संभालने का एकमात्र तरीका यह है कि आप उन्हें उम्मीद कर सकें। यदि आपका डेटाबेस कोड अच्छी तरह लिखा गया है तो यह आमतौर पर बहुत मुश्किल नहीं है। अक्सर आप क्वेरी निष्पादन तर्क के आस-पास try/catch डाल सकते हैं और त्रुटियों के दौरान एक डेडलॉक की तलाश कर सकते हैं। यदि आप एक को पकड़ते हैं, तो सामान्य बात यह है कि असफल क्वेरी को फिर से निष्पादित करने का प्रयास करें।

मैं आपको सलाह देता हूं कि आप MySQL मैनुअल में this page पढ़ें। इसमें डेडलॉक्स से निपटने और उनकी आवृत्ति को कम करने में मदद करने के लिए चीजों की एक सूची है।

+2

हमें कौन से त्रुटि कोडों को पकड़ने की आवश्यकता है? अकेले 1205 पकड़ रहा है? Http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html में 900 से अधिक त्रुटि कोड हैं। आप अपने सभी प्रयासों को कैसे जानते हैं जिन्हें हमें आपके प्रयास/पकड़ सुझाव के लिए उचित समाधान लागू करने के लिए पकड़ने की आवश्यकता है? – Pacerier

+0

इसका मतलब यह है कि इन समस्याओं की जरूरत नहीं है 'InnoDB या किसी पंक्ति-स्तर व्यवहार RDBMS' के अलावा अन्य? –

5

ध्यान दें कि यदि आप डालने से पहले एक विशिष्टता जांच करने के लिए SELECT FOR UPDATE का उपयोग करते हैं, तो आप प्रत्येक रेस स्थिति के लिए एक डेडलॉक प्राप्त करेंगे जबतक कि आप innodb_locks_unsafe_for_binlog विकल्प सक्षम नहीं करते हैं। विशिष्टता की जांच करने के लिए एक डेडलॉक-मुक्त विधि INSERT IGNORE का उपयोग करके एक अद्वितीय अनुक्रमणिका के साथ एक तालिका में अंधेरे से एक पंक्ति डालना है, फिर प्रभावित पंक्ति गणना की जांच करने के लिए।पर
0 - -

my.cnf फ़ाइल

innodb_locks_unsafe_for_binlog = 1

#

1 करने के लिए रेखा से नीचे जोड़ने बंद

#
+0

यह मल्टीथ्रेडेड वातावरण में ActiveRecord एसोसिएशन को सहेजने के साथ मेरे सभी मुद्दों को हल करता है। – lightyrs

+2

को सक्षम करने से 'innodb_locks_unsafe_for_binlog' प्रेत समस्या पैदा हो सकती है क्योंकि अन्य सत्र के अंतराल में नई पंक्तियाँ सम्मिलित कर सकते हैं जब खाई लॉकिंग अक्षम है। – shivam

9

जवाब सही है, लेकिन पर्ल प्रलेखन डेडलॉक्स को संभालने के तरीके पर थोड़ा सा स्पैस है और शायद प्रिंटरर, राइज एरर और हैंडलरर के साथ भ्रमित है विकल्प। ऐसा लगता है कि हैंडलरर के साथ जाने के बजाय, प्रिंट और राइज पर उपयोग करें और फिर कोशिश करें जैसे कुछ कोशिश करें: अपने कोड को लपेटने के लिए छोटे और त्रुटियों की जांच करें। नीचे दिया गया कोड एक उदाहरण देता है जहां डीबी कोड थोड़ी देर के अंदर है जो प्रत्येक 3 सेकंड में एक गलत एसक्यूएल स्टेटमेंट को फिर से निष्पादित करेगा। कैच ब्लॉक $ _ हो जाता है जो विशिष्ट त्रुटि संदेश है। मैं इसे एक हैंडलर फ़ंक्शन "dbi_err_handler" पर पास करता हूं जो कई त्रुटियों के विरुद्ध $ _ की जांच करता है और यदि कोड जारी रखना चाहिए (इस प्रकार लूप को तोड़ना चाहिए) या 0 यदि यह एक डेडलॉक है और उसे पुनः प्रयास किया जाना चाहिए ...

$sth = $dbh->prepare($strsql); 
my $db_res=0; 
while($db_res==0) 
{ 
    $db_res=1; 
    try{$sth->execute($param1,$param2);} 
    catch 
    { 
     print "caught $_ in insertion to hd_item_upc for upc $upc\n"; 
     $db_res=dbi_err_handler($_); 
     if($db_res==0){sleep 3;} 
    } 
} 

dbi_err_handler कम से कम निम्नलिखित करना चाहिए:

sub dbi_err_handler 
{ 
    my($message) = @_; 
    if($message=~ m/DBD::mysql::st execute failed: Deadlock found when trying to get lock; try restarting transaction/) 
    { 
     $caught=1; 
     $retval=0; # we'll check this value and sleep/re-execute if necessary 
    } 
    return $retval; 
} 

आप अन्य त्रुटियों आप फिर से निष्पादित या जारी रखना चाहते हैं, इस आधार पर सेट $ retval .. आप को संभाल और करना चाहते हैं को शामिल करना चाहिए

उम्मीद है कि यह किसी की मदद करता है -

0

पुन: प्रयास करने के विचार डेडलॉक अपवाद के मामले में क्वेरी अच्छा है, लेकिन यह बहुत धीमी गति से हो सकता है, के बाद से mysql क्वेरी इंतजार कर ताले रिलीज होने के लिए रखेंगे। और गतिरोध mysql के बैठाना अगर कोई गतिरोध है, और जानने के लिए एक गतिरोध है कि वहाँ के बाद भी यह क्रम गतिरोध स्थिति से बाहर निकलने के लिए में एक धागा बाहर लात से पहले थोड़ी देर के इंतजार कर रहा है खोजने की कोशिश कर रहा है। के बाद से यह mysql का ताला तंत्र बग के कारण विफल हो रहा है है

मैं क्या किया था जब मैं इस स्थिति का सामना करना पड़ा, अपने खुद के कोड में ताला लगा लागू करने के लिए है। इसलिए मैंने अपने जावा कोड में अपनी खुद की पंक्ति स्तर लॉकिंग लागू की:

private HashMap<String, Object> rowIdToRowLockMap = new HashMap<String, Object>(); 
private final Object hashmapLock = new Object(); 
public void handleShortCode(Integer rowId) 
{ 
    Object lock = null; 
    synchronized(hashmapLock) 
    { 
     lock = rowIdToRowLockMap.get(rowId); 
     if (lock == null) 
     { 
      rowIdToRowLockMap.put(rowId, lock = new Object()); 
     } 
    } 
    synchronized (lock) 
    { 
     // Execute your queries on row by row id 
    } 
} 
+4

दुर्भाग्य से यह संभावना है कि अधिकांश उपयोगकर्ताओं को इस का सामना कई मशीनों या एक ही MySQL उदाहरण में डेटा डंपिंग प्रक्रियाओं के साथ काम कर रहे हैं। एप्लिकेशन में पंक्ति स्तर लॉकिंग ज्यादातर उपयोगकर्ताओं के लिए एक विकल्प नहीं है। – dgtized

संबंधित मुद्दे

 संबंधित मुद्दे