प्रस्तावना
हमारा एप्लिकेशन DELETE
समानांतर में प्रश्नों को निष्पादित करने वाले कई थ्रेड चलाता है । प्रश्न पृथक डेटा को प्रभावित करते हैं, अर्थात ऐसी कोई संभावना नहीं होनी चाहिए कि DELETE
अलग-अलग थ्रेड्स से समान पंक्तियों पर समवर्ती हो । हालांकि, प्रति प्रलेखन MySQL DELETE
बयानों के लिए तथाकथित 'अगली-कुंजी' लॉक का उपयोग करता है , जो मिलान कुंजी और कुछ अंतराल दोनों को लॉक करता है। यह चीज़ डेड-लॉक की ओर ले जाती है और एकमात्र समाधान जो हमने पाया है वह READ COMMITTED
आइसोलेशन स्तर का उपयोग करना है।
समस्या
भारी तालिकाओं के DELETE
साथ जटिल कथनों को निष्पादित करते समय समस्या उत्पन्न होती है JOIN
। किसी विशेष मामले में हमारे पास चेतावनी के साथ एक तालिका है जिसमें केवल दो पंक्तियाँ हैं, लेकिन क्वेरी को उन सभी चेतावनियों को छोड़ने की आवश्यकता है जो दो अलग-अलग INNER JOIN
एड तालिकाओं से कुछ विशेष संस्थाओं से संबंधित हैं । क्वेरी निम्नानुसार है:
DELETE pw
FROM proc_warnings pw
INNER JOIN day_position dp
ON dp.transaction_id = pw.transaction_id
INNER JOIN ivehicle_days vd
ON vd.id = dp.ivehicle_day_id
WHERE vd.ivehicle_id=? AND dp.dirty_data=1
जब दिन_पोजिशन टेबल काफी बड़ी होती है (मेरे परीक्षण के मामले में 1448 पंक्तियाँ होती हैं) तो READ COMMITTED
अलगाव मोड के साथ कोई भी लेन-देन पूरी proc_warnings
तालिका को ब्लॉक कर देता है ।
इस नमूना डेटा पर समस्या को हमेशा पुन: प्रस्तुत किया जाता है - http://yadi.sk/d/QDuwBtpW1BxB9 दोनों MySQL 5.1 (5.1.59 पर जांचा गया) और MySQL 5.5 (MySQL 5.5.24 पर जाँच) में।
संपादित करें: लिंक किए गए नमूना डेटा में क्वेरी तालिकाओं के लिए स्कीमा और अनुक्रमित शामिल हैं, जो सुविधा के लिए यहां प्रस्तुत किए गए हैं:
CREATE TABLE `proc_warnings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`transaction_id` int(10) unsigned NOT NULL,
`warning` varchar(2048) NOT NULL,
PRIMARY KEY (`id`),
KEY `proc_warnings__transaction` (`transaction_id`)
);
CREATE TABLE `day_position` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`transaction_id` int(10) unsigned DEFAULT NULL,
`sort_index` int(11) DEFAULT NULL,
`ivehicle_day_id` int(10) unsigned DEFAULT NULL,
`dirty_data` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `day_position__trans` (`transaction_id`),
KEY `day_position__is` (`ivehicle_day_id`,`sort_index`),
KEY `day_position__id` (`ivehicle_day_id`,`dirty_data`)
) ;
CREATE TABLE `ivehicle_days` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`d` date DEFAULT NULL,
`sort_index` int(11) DEFAULT NULL,
`ivehicle_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ivehicle_days__is` (`ivehicle_id`,`sort_index`),
KEY `ivehicle_days__d` (`d`)
);
प्रति लेनदेन क्वेरी निम्नानुसार हैं:
लेन-देन १
set transaction isolation level read committed; set autocommit=0; begin; DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=2 AND dp.dirty_data=1;
लेन-देन २
set transaction isolation level read committed; set autocommit=0; begin; DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1;
उनमें से एक हमेशा 'लॉक वेट टाइमआउट पार हो गया ...' त्रुटि के साथ विफल रहता है। information_schema.innodb_trx
निम्नलिखित पंक्तियां हैं:
| trx_id | trx_state | trx_started | trx_requested_lock_id | trx_wait_started | trx_wait | trx_mysql_thread_id | trx_query |
| '1A2973A4' | 'LOCK WAIT' | '2012-12-12 20:03:25' | '1A2973A4:0:3172298:2' | '2012-12-12 20:03:25' | '2' | '3089' | 'DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1' |
| '1A296F67' | 'RUNNING' | '2012-12-12 19:58:02' | NULL | NULL | '7' | '3087' | NULL |
information_schema.innodb_locks
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
| '1A2973A4:0:3172298:2' | '1A2973A4' | 'X' | 'RECORD' | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
| '1A296F67:0:3172298:2' | '1A296F67' | 'X' | 'RECORD' | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
जैसा कि मैं देख सकता हूं कि दोनों प्रश्न X
प्राथमिक कुंजी = 53 के साथ एक पंक्ति पर एक विशेष लॉक चाहते हैं । हालांकि, उनमें से किसी को भी proc_warnings
तालिका से पंक्तियों को नहीं हटाना चाहिए । मुझे अभी समझ में नहीं आया है कि सूचकांक क्यों बंद है। इसके अलावा, इंडेक्स को तब भी लॉक नहीं किया जाता है जब proc_warnings
टेबल खाली हो या day_position
टेबल में पंक्तियों की संख्या कम हो (यानी एक सौ पंक्तियाँ)।
आगे की जांच EXPLAIN
समान SELECT
क्वेरी पर चलने की थी । यह दिखाता है कि क्वेरी ऑप्टिमाइज़र क्वेरी proc_warnings
टेबल पर इंडेक्स का उपयोग नहीं करता है और यही एकमात्र कारण है जिसकी मैं कल्पना कर सकता हूं कि यह पूरे प्राथमिक कुंजी इंडेक्स को ब्लॉक क्यों करता है।
सरलीकृत मामला
समस्या को एक सरल मामले में भी पुन: प्रस्तुत किया जा सकता है, जब कुछ दो ही तालिकाएँ होती हैं, लेकिन चाइल्ड टेबल में पैरेंट टेबल रेफरी कॉलम पर कोई इंडेक्स नहीं होता है।
parent
तालिका बनाएं
CREATE TABLE `parent` (
`id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
child
तालिका बनाएं
CREATE TABLE `child` (
`id` int(10) unsigned NOT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
टेबल भरें
INSERT INTO `parent` (id) VALUES (1), (2);
INSERT INTO `child` (id, parent_id) VALUES (1, NULL), (2, NULL);
दो समानांतर लेनदेन में परीक्षण:
लेन-देन १
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET AUTOCOMMIT=0; BEGIN; DELETE c FROM child c INNER JOIN parent p ON p.id = c.parent_id WHERE p.id = 1;
लेन-देन २
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET AUTOCOMMIT=0; BEGIN; DELETE c FROM child c INNER JOIN parent p ON p.id = c.parent_id WHERE p.id = 2;
दोनों मामलों में आम बात यह है कि MySQL सूचकांकों का उपयोग नहीं करता है। मेरा मानना है कि पूरे टेबल के लॉक का कारण है।
हमारा समाधान
एकमात्र समाधान जिसे हम अभी देख सकते हैं, थ्रेड फिनिश को साफ करने के लिए डिफ़ॉल्ट लॉक प्रतीक्षा समय 50 सेकंड से 500 सेकंड तक बढ़ा सकते हैं। फिर उंगलियों को पार करते रहें।
किसी भी मदद की सराहना की।
day_position
टेबल में सामान्य रूप से कितनी पंक्तियाँ होती हैं, जब यह इतनी धीमी गति से चलने लगती है कि आपको टाइमआउट की सीमा 500 सेकंड तक काटनी पड़ती है? 2) जब आपके पास केवल नमूना डेटा है, तो इसे चलाने में कितना समय लगेगा?