InnoDB DELETE प्रदर्शन कैसे सुधारें?


9

इसलिए मेरे पास यह ऑडिट टेबल है (मेरे डेटाबेस में किसी भी टेबल पर कार्रवाई को ट्रैक करता है):

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

और मुझे पुरानी वस्तुओं को संग्रहीत करना शुरू करना होगा। तालिका लगभग 50 मिलियन बिलियन पंक्तियों तक बढ़ गई है, इसलिए मैं सबसे तेजी से जिस तरह से पंक्तियों को हटा सकता था, उसे एक बार (तालिका के आधार पर tableName) हटाना था ।

यह बहुत अच्छी तरह से काम करता है, लेकिन कुछ टेबल्स जो लिखने में भारी हैं, यह पूरा नहीं होगा। मेरी क्वेरी उन सभी आइटमों को हटा देती है जिन deleteपर एक tupleID / tableName संयोजन पर संबंधित कार्रवाई होती है:

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

मैंने इसे 3 दिनों के लिए अपने सर्वर पर चलने दिया और यह सबसे बड़ी तालिका के लिए कभी पूरा नहीं हुआ। व्याख्या आउटपुट (यदि मैं हटाने के लिए चयन करने के लिए स्विच करता हूं:

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

इसलिए 4 मिलियन पंक्तियों को हटाने के लिए 3 दिन नहीं चाहिए, मुझे लगता है। मेरे पास 3GB में मेरा innodb_buffer_pool_size सेट है, और सर्वर one_file_per_table का उपयोग करने के लिए सेट नहीं है। मैं InnoDB प्रदर्शन को हटाने के लिए किन अन्य तरीकों से सुधार कर सकता हूं? (मैक ओएसएक्स पर MySQL 5.1.43 रनिंग)

जवाबों:


11

आप बैचों में डेटा हटा सकते हैं।

SQL सर्वर में, सिंटैक्स delete top Xएक तालिका से पंक्तियाँ होती हैं। आप इसे एक लूप में करते हैं, प्रत्येक बैच के लिए लेनदेन के साथ (यदि आपके पास एक से अधिक कथन है, तो निश्चित रूप से), इसलिए लेन-देन को कम रखने के लिए और केवल छोटी अवधि के लिए ताले बनाए रखें।

MySQL सिंटैक्स में: DELETE FROM userTable LIMIT 1000

उस पर प्रतिबंध हैं ( LIMITउदाहरण के लिए, जुड़ने के साथ हटाने में उपयोग नहीं किया जा सकता ) लेकिन इस मामले में आप इसे इस तरह से करने में सक्षम हो सकते हैं।

वहाँ का उपयोग कर के लिए एक अतिरिक्त खतरा है LIMITसाथ DELETEजब यह प्रतिकृति की बात आती है; हटाए गए पंक्तियों को कभी-कभी दास पर उसी क्रम में हटाया नहीं जाता है जैसा कि इसे मास्टर पर हटा दिया गया था।


6

एक अस्थायी तालिका दृष्टिकोण का उपयोग करने का प्रयास करें। कुछ इस तरह की कोशिश करो:

चरण 1) CREATE TABLE track_table_new LIKE track_table;

चरण 2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

चरण 3) ALTER TABLE track_table RENAME track_table_old;

चरण 4) ALTER TABLE track_table_new RENAME track_table;

चरण 5) DROP TABLE track_table_old;

मैंने चरण 2 में टुपल फ़ील्ड को शामिल नहीं किया है। कृपया देखें कि क्या यह वांछित प्रभाव पैदा करता है। यदि यह वही है जो आप चाहते हैं, तो आप टुपल फ़ील्ड को पूरी तरह से खोदना चाहते हैं जब तक कि आप अन्य कारणों से टपल फ़ील्ड का उपयोग न करें।


यह एक दिलचस्प उपाय है। मुझे तालिका में टुपल फ़ील्ड की आवश्यकता है। tableName / tupleID उस तालिका की अपरिभाषित विदेशी कुंजी है जिसे लॉग किया जा रहा है। अनिर्धारित क्योंकि कुछ समय पहले तक, यह तालिका MyISAM थी, जो विदेशी कुंजी का समर्थन नहीं करती है।
डेरेक डाउनी

1

बैच में अवांछित पंक्तियों का विलोपन अन्य संचालन को कार्यशील रखना चाहिए। लेकिन आपके ऑपरेशन को हटाने की शर्तें हैं, इसलिए सुनिश्चित करें कि शर्तों पर कॉलम पर एक उपयुक्त सूचकांक है।

क्योंकि MySQL ढीला सूचकांक स्कैन की पूरी समारोह का समर्थन नहीं करता है, तो आप के लिए अनुक्रम समायोजित करने के लिए कोशिश कर सकते हैं KEY actionDate (action, date_insert)करने के लिए KEY actionDate (date_insert, action)। 'Date_insert' के उपसर्ग के साथ, MySQL को इस सूचकांक का उपयोग उन पंक्तियों को स्कैन करने के लिए करना चाहिए जो आपकी डेटटाइम स्थिति से पहले हैं।

इस तरह के सूचकांक के साथ, आप एसक्यूएल लिख सकते हैं:

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch

1
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-फिर, आपके समझाने से की_लेन इतना बड़ा => आपको जितना संभव हो उतना छोटा आकार अपग्रेड करने की आवश्यकता है। आपकी क्वेरी के लिए मुझे लगता है कि सबसे अच्छा तरीका डेटा प्रकार की कार्रवाई फ़ील्ड को चार (12) से टिंटिंट तक बदलना है, इसलिए डेटा मैपिंग देखो:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

और आप tablename के बजाय table_id को बदल सकते हैं। सबसे अच्छा प्रदर्शन के लिए DDL कर सकते हैं:

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

इसलिए क्वेरी चल सकती है जैसे कि:

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

लेकिन सबसे तेज़ तरीका विभाजन का उपयोग कर रहा था। तो आप विभाजन को छोड़ सकते हैं। वर्तमान में, मेरी तालिका में लगभग 40 मिलियन से अधिक पंक्तियाँ हैं। और प्रति घंटा अपडेट करें (प्रत्येक समय के लिए 400k पंक्तियों को अपडेट करें), और मैं cur_date विभाजन को ड्रॉप कर सकता हूं और डेटा को तालिका में पुनः लोड कर सकता हूं। ड्रॉप कमांड बहुत तेज (<100ms)। उममीद है कि इससे मदद मिलेगी।

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