คำสั่ง DELETE ไม่เสร็จในตาราง 30,000,000 แถว


22

ฉันได้รับฐานข้อมูลและต้องการทำความสะอาดและเร่งความเร็ว ฉันมีตารางที่มีแถว 30,000,000 แถวซึ่งส่วนใหญ่เป็นข้อมูลขยะที่แทรกเนื่องจากข้อผิดพลาดในนามของโปรแกรมเมอร์ของเรา ก่อนที่ฉันจะเพิ่มดัชนีใหม่ที่ได้รับการปรับปรุงให้ดีขึ้นฉันจะแปลงตารางจาก MyISAM เป็น InnoDB และฉันต้องการลบแถวจำนวนมากที่มีข้อมูลขยะ

ฐานข้อมูลคือ MySQL 5.0 และฉันมีสิทธิ์เข้าถึงรูทไปยังเซิร์ฟเวอร์ ฉันใช้งานคำสั่งเหล่านี้เป็นครั้งแรกผ่านทาง Adminer จากนั้น phpMyAdmin ทั้งคู่ก็มีผลลัพธ์เหมือนกัน

คำสั่งที่ฉันใช้คือ

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

-เป็นหลักอะไรลบในคอลัมน์นี้ที่เริ่มต้นด้วยเส้นประ

มันใช้เวลาประมาณ 3-5 นาทีและเมื่อฉันดูรายการกระบวนการมันก็หายไป

จากนั้นฉันก็วิ่ง

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

และจะส่งคืนแถวนับล้าน

ทำไมคำสั่งลบของฉันจึงไม่เสร็จ

PS, ฉันรู้ว่า MySQL 5.0 ล้าสมัยแล้วอย่างไร ฉันกำลังทำงานเกี่ยวกับการย้ายฐานข้อมูลไปยัง MySQL 5.6 w InnoDB (อาจเป็น MariaDB 10 w XtraDB) แต่จนกว่าจะเกิดเหตุการณ์นี้ขึ้นฉันกำลังหาคำตอบด้วย DB ตามที่เป็นอยู่

-

แก้ไขลบออกดูคำตอบของฉัน

คำตอบ:


24

โปรดดูสถาปัตยกรรมของ InnoDB (ภาพจาก Percona CTO Vadim Tkachenko)

ท่อ InnoDB

แถวที่คุณกำลังลบกำลังถูกเขียนลงในเลิกทำบันทึก ไฟล์ ibdata1 ควรเติบโตในขณะนี้ในช่วงระยะเวลาของการลบ ตามmysqlperformanceblog.com'sReasons for run-away main Innodb Tablespace :

  • การเปลี่ยนแปลงธุรกรรมมากมาย
  • การทำธุรกรรมที่ยาวนานมาก
  • Lagging Purge Thread

ในกรณีของคุณเหตุผลที่ # 1 จะครอบครองเซ็กเมนต์การย้อนกลับหนึ่งส่วนพร้อมกับพื้นที่เลิกทำบางส่วนเนื่องจากคุณกำลังลบแถว แถวเหล่านั้นต้องอยู่ใน ibdata1 จนกว่าการลบจะเสร็จสิ้น พื้นที่นั้นถูกทิ้งอย่างมีเหตุผล แต่พื้นที่ไม่หดกลับ

คุณต้องฆ่าการลบนั้นในตอนนี้ เมื่อคุณฆ่าแบบสอบถามลบลบมันจะย้อนกลับแถวที่ถูกลบ

คุณทำสิ่งนี้แทน:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

คุณสามารถทำสิ่งนี้กับเวอร์ชัน MyISAM ของตารางก่อน จากนั้นแปลงเป็น InnoDB


21

ผมคิดว่าเราอาจจะ overcomplicated คำตอบที่ถูกต้องในในกรณีของฉัน ฉันไม่สงสัยเลยว่าทั้ง Roland & Rick James นั้นถูกต้องกับการสร้างตารางชั่วคราวฉีดแถวที่ผ่านตัวกรองNOT LIKE '-%'เท่านั้น แต่ทางออกสำหรับฉันคือ "ง่ายขึ้น" เพราะมีข้อผิดพลาดที่สำคัญที่ฉันไม่รู้จนกระทั่งตอนนี้และสำหรับ ที่ฉันขอโทษ

ฉันเรียกใช้แบบสอบถามในmysqlพรอมต์แบบโต้ตอบและสังเกตเห็นข้อความแสดงข้อผิดพลาด

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

ผ่าน googleing ข้อผิดพลาดที่ฉันพบโซลูชันเพื่อเพิ่มinnodb_buffer_pool_sizeผ่านทาง/etc/my.cnfไฟล์และรีบูตภูต MySQL สำหรับเซิร์ฟเวอร์ของฉันมันถูกตั้งค่าเป็นค่าเริ่มต้น8Mและฉันเพิ่มเป็น1G(เซิร์ฟเวอร์มี 32GB และนี่เป็นตารางเดียวที่ปัจจุบันคือ InnoDB)

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

จากนั้นฉันสามารถเรียกใช้คำสั่งและลบ 23 ล้านเร็กคอร์ดใน ~ 27 นาที

สำหรับผู้ที่อยากรู้อยากเห็นสิ่งที่innodb_buffer_pool_sizeควรตั้งค่าให้จดจำนวน RAM ที่คุณมีแล้วดูหัวข้อนี้ที่ให้การประมาณที่แนะนำในหน่วย GB


12

ข้อเสนอแนะของ Roland สามารถเร่งบางส่วนได้โดยทำทั้งสองอย่างพร้อมกัน:

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

แต่นี่คือบล็อกที่อธิบายวิธีทำ DELETE ขนาดใหญ่ในกลุ่มแทนที่จะใช้เวลาตลอดไป: http://mysql.rjweb.org/doc.php/deletebig ส่วนสำคัญคือการเดินผ่านตารางผ่าน PK โดยทำ 1K แถวในครั้งเดียว (แน่นอนมีรายละเอียดเพิ่มเติมที่ต้องระวัง)

และบล็อกนี้ได้กล่าวถึง gotchas ที่อาจเกิดขึ้นในการแปลงเป็น InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb


5

สัญชาตญาณแรกของฉันคือทำการลบหลาย ๆ อันเล็กลงโดย จำกัด จำนวนผลลัพธ์ของแบบสอบถามและเรียกใช้แบบสอบถามหลายครั้ง:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000

ข้อเสียเปรียบของวิธีนี้: การลบแต่ละครั้งจะใช้เวลานานและนานขึ้น WHEREนี้เป็นเพราะความต้องการที่จะข้ามแถวมากขึ้นและมากขึ้นที่ไม่ตรงกับ
Rick James

จริง แต่ถ้ากระบวนการนี้ไม่เกิดขึ้นบ่อยเกินไปการสแกนตารางที่สมบูรณ์หลายรายการไม่ควรเลวร้ายเท่าปัญหาเดิมที่กำลังแก้ไขซึ่งเป็นที่แบบสอบถามไม่เคยเสร็จสมบูรณ์เนื่องจากขนาดล็อกที่เลิกทำ
kristianp

จุดที่ถูกต้อง (ฉันจะทำให้LIMITลดลงพูด 10000)
ริกเจมส์

4

ทางออกที่ง่ายที่สุดคือการไม่ทำอย่างนั้นทำลบขนาดเล็กลงซึ่งสามารถประมวลผลได้ง่ายขึ้น

ในกรณีนี้ฉันจะแนะนำให้ลองลบตามลำดับของแบบฟอร์ม:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'

2

บางทีคุณอาจทำสิ่งนี้:

  • deletedเพิ่มสาขาใหม่ที่เรียกว่า
  • UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'จะปรับปรุงเช่น
  • ตั้งค่าcronให้ลบสิ่งนี้ในเวลากลางคืน

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