จะปรับปรุงประสิทธิภาพของ 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 วันในการลบฉันคิดว่า ฉันมี innodb_buffer_pool_size ตั้งไว้ที่ 3GB และเซิร์ฟเวอร์ไม่ได้ถูกตั้งค่าให้ใช้ one_file_per_table ฉันสามารถปรับปรุงประสิทธิภาพการลบ InnoDB ได้ด้วยวิธีใดอีกบ้าง (ใช้ MySQL 5.1.43 บน Mac OSX)

คำตอบ:


11

คุณสามารถลบข้อมูลเป็นชุด

ใน SQL Server ไวยากรณ์คือdelete top Xแถวจากตาราง จากนั้นคุณทำในลูปโดยมีธุรกรรมสำหรับแต่ละแบทช์ (ถ้าคุณมีคำสั่งมากกว่าหนึ่งคำสั่ง) ดังนั้นเพื่อให้ธุรกรรมสั้นและรักษาล็อกไว้สำหรับช่วงเวลาสั้น ๆ เท่านั้น

ในไวยากรณ์ MySQL: DELETE FROM userTable LIMIT 1000

มีข้อ จำกัด ในเรื่องนั้น (ไม่สามารถใช้LIMITในการลบด้วยการรวมเช่น) แต่ในกรณีนี้คุณอาจทำเช่นนั้นได้

มีอันตรายเพิ่มเติมที่จะใช้LIMITกับDELETEเมื่อมันมาถึงการจำลองแบบคือ; บางครั้งแถวที่ถูกลบจะไม่ถูกลบในลำดับเดียวกันบนสลาฟเนื่องจากถูกลบบนมาสเตอร์


6

ลองใช้วิธี temp table ลองสิ่งนี้:

ขั้นตอนที่ 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;

ฉันไม่ได้รวมฟิลด์ tuple ในขั้นตอนที่ 2 โปรดดูว่าสิ่งนี้ให้ผลที่ต้องการหรือไม่ หากนี่คือสิ่งที่คุณต้องการคุณอาจต้องการทิ้งฟิลด์ tuple โดยสิ้นเชิงเว้นแต่คุณจะใช้ฟิลด์ tuple ด้วยเหตุผลอื่น


นั่นเป็นทางออกที่น่าสนใจ ฉันต้องการฟิลด์ tuple ในตาราง tableName / tupleID เป็น foreign key ที่ไม่ได้กำหนดของตารางที่ถูกบันทึกไว้ ไม่ได้กำหนดเนื่องจากจนกระทั่งเมื่อเร็ว ๆ นี้ตารางนี้คือ MyISAM ซึ่งไม่รองรับคีย์ต่างประเทศ
Derek Downey

1

การลบแถวที่ไม่ต้องการในชุดควรทำให้การทำงานอื่นสามารถใช้งานได้ แต่การลบการดำเนินการของคุณมีเงื่อนไขดังนั้นให้แน่ใจว่ามีดัชนีที่เหมาะสมในคอลัมน์เหนือเงื่อนไข

เพราะ MySQL ไม่สนับสนุนการทำงานที่สมบูรณ์ของดัชนีสแกนหลวมคุณอาจลองปรับลำดับสำหรับการKEY actionDate (action, date_insert) KEY actionDate (date_insert, action)ด้วยคำนำหน้าของ 'date_insert' MySQL ควรใช้ดัชนีนี้เพื่อสแกนแถวที่อยู่ก่อนเงื่อนไขวันที่และเวลาของคุณ

ด้วยดัชนีดังกล่าวคุณอาจเขียน SQL เป็น:

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 |

- กำปั้นจากการอธิบาย key_len คุณใหญ่มาก => คุณต้องลดขนาดให้เล็กที่สุดเท่าที่จะทำได้ สำหรับคำถามของคุณฉันคิดว่าวิธีที่ดีที่สุดคือเปลี่ยนประเภทข้อมูลของเขตข้อมูลการดำเนินการจากถ่าน (12) เป็นขนาดเล็กดังนั้นการแมปข้อมูลจะมีลักษณะดังนี้:

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

และคุณสามารถเปลี่ยน table_id แทน tablename ได้เช่นกัน 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 แถวปรับปรุงในแต่ละครั้ง) และฉันสามารถวางพาร์ทิชัน curr_date และโหลดข้อมูลลงในตาราง คำสั่ง drop เร็วมาก (<100ms) หวังว่าความช่วยเหลือนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.