DELETE-Befehl für 30.000.000-Zeilentabelle nicht abgeschlossen

22

Ich habe eine Datenbank geerbt und möchte diese bereinigen und beschleunigen. Ich habe eine Tabelle mit 30.000.000 Zeilen, von denen viele Junk-Daten sind, die aufgrund eines Fehlers im Auftrag unseres Programmierers eingefügt wurden. Bevor ich neue, optimierte Indizes hinzufüge, habe ich die Tabelle von MyISAM nach InnoDB konvertiert und möchte viele der Zeilen löschen, die Junk-Daten enthalten.

Die Datenbank ist MySQL 5.0 und ich habe root-Zugriff auf den Server. Ich habe diese Befehle zuerst über Adminer und dann über phpMyAdmin ausgeführt, beide mit den gleichen Ergebnissen.

Der Befehl, den ich ausführe, lautet:

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

Löschen Sie im Wesentlichen alle Elemente in dieser Spalte, die mit einem Bindestrich beginnen -.

Es läuft ca. 3-5 Minuten und wenn ich dann die Prozessliste ansehe, ist es weg.

Ich renne dann,

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

und es gibt Millionen von Zeilen zurück.

Warum wird meine Löschanweisung nicht ausgeführt?

PS, mir ist bekannt, wie veraltet MySQL 5.0 ist. Ich arbeite daran, die Datenbank auf MySQL 5.6 mit InnoDB (vielleicht MariaDB 10 mit XtraDB) umzustellen, aber bis dies passiert, versuche ich, dies mit der Datenbank so zu beantworten, wie sie ist.

-

Bearbeiten entfernt, siehe meine Antwort.

bafromca
quelle

Antworten:

24

Bitte schauen Sie sich die Architektur von InnoDB an (Bild von Percona CTO Vadim Tkachenko)

InnoDB Sanitär

Die Zeilen, die Sie löschen, werden in die Rückgängig-Protokolle geschrieben. Die Datei ibdata1 sollte jetzt für die Dauer des Löschvorgangs wachsen. Laut mysqlperformanceblog.com istReasons for run-away main Innodb Tablespace :

  • Viele Transaktionsänderungen
  • Sehr lange Transaktionen
  • Nachlaufender Spülfaden

In Ihrem Fall würde Grund 1 ein Rollback-Segment zusammen mit einem Teil des Rückgängig-Raums belegen, da Sie Zeilen löschen. Diese Zeilen müssen sich in ibdata1 befinden, bis der Löschvorgang abgeschlossen ist. Dieser Speicherplatz wird logisch verworfen, aber der Speicherplatz wird nicht verkleinert.

Sie müssen diesen Löschvorgang sofort beenden. Sobald Sie die Löschabfrage beendet haben, werden die gelöschten Zeilen zurückgesetzt.

Sie tun dies stattdessen:

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;

Sie hätten dies zuerst mit der MyISAM-Version der Tabelle tun können. Dann konvertieren Sie es in InnoDB.

RolandoMySQLDBA
quelle
21

Ich denke, wir haben die in meinem Fall erforderliche Antwort möglicherweise zu kompliziert . Ich habe keinen Zweifel, dass beide, Roland und Rick James, mit der Erstellung einer temporären Tabelle richtig liegen und nur Zeilen einfügen, die den Filter passieren, NOT LIKE '-%'aber die Lösung war für mich "einfacher", da es einen wichtigen Fehler gab, den ich bis jetzt und für heute nicht kannte dass ich mich entschuldige.

Ich habe die Abfrage in der mysqlinteraktiven Eingabeaufforderung ausgeführt und die Fehlermeldung festgestellt.

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

Durch Googleing des Fehlers fand ich die Lösung , innodb_buffer_pool_sizeüber die /etc/my.cnfDatei zu erhöhen und den MySQL-Daemon neu zu starten. Für meinen Server wurde der Standardwert festgelegt 8Mund ich habe ihn auf erhöht 1G(der Server hat 32 GB und dies ist die einzige Tabelle, die derzeit InnoDB ist).

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

Dann konnte ich den Befehl ausführen und 23 Millionen Datensätze in ca. 27 Minuten löschen.

Für diejenigen, die neugierig sind, was eingestellt innodb_buffer_pool_sizewerden soll, notieren Sie sich, wie viel RAM Sie haben, und schauen Sie sich dann diesen Thread an , der eine empfohlene Schätzung in GB angibt.

bafromca
quelle
12

Rolands Vorschlag kann beschleunigt werden, indem beide Dinge gleichzeitig ausgeführt werden:

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;

Aber hier ist ein Blog, in dem erklärt wird, wie man große DELETEs in Stücken ausführt, anstatt scheinbar für immer zu dauern: http://mysql.rjweb.org/doc.php/deletebig Das Wesentliche ist, über die PK durch den Tisch zu gehen und dabei 1K zu machen Reihen auf einmal. (Natürlich sind weitere Details zu beachten.)

Und dieser Blog befasst sich mit potenziellen Problemen bei der Umstellung auf InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb

Rick James
quelle
5

Mein erster Gedanke wäre, mehrere kleinere Löschvorgänge durchzuführen, indem ich die Anzahl der Abfrageergebnisse beschränke und die Abfrage mehrmals ausführe:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000
kristianp
quelle
Ein Nachteil dieses Ansatzes: Jedes Löschen dauert länger und länger. Dies liegt daran, dass immer mehr Zeilen übersprungen werden müssen, die nicht mit den übereinstimmen WHERE.
Rick James
Richtig, aber wenn dieser Prozess nicht zu oft vorkommt, sollten mehrere vollständige Tabellenscans nicht so schlimm sein wie das ursprüngliche Problem, das gelöst wird. Das heißt, die Abfrage wird aufgrund der Größe des Rückgängigmachens des Protokolls nie abgeschlossen.
Kristianp
Gutes Argument. (Ich würde den LIMITniedrigeren machen; sagen wir 10000.)
Rick James
4

Die einfachste Lösung ist, dies einfach nicht zu tun - einen kleineren Löschvorgang durchzuführen, der einfacher zu verarbeiten ist.

In diesem Fall hätte ich empfohlen, sequentielles Löschen des Formulars zu versuchen:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'
jmoreno
quelle
2

Vielleicht könntest du so etwas machen:

  • Fügen Sie ein neues Feld mit dem Namen hinzu deleted.
  • Mach ein Update wie UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Stellen Sie ein cron, um dies nachts zu löschen.
Mike Minaev
quelle
Das Update kann so lange dauern wie das Löschen.
Rick James