it-swarm.com.de

Der Befehl DELETE wird für 30.000.000 Zeilentabellen nicht ausgeführt

Ich habe eine Datenbank geerbt und möchte sie 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 in 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 denselben Ergebnissen.

Der Befehl, den ich ausführe, lautet:

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

Löschen Sie im Wesentlichen alles in dieser Spalte, was mit einem Bindestrich - Beginnt.

Es läuft ungefähr 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 abgeschlossen?

PS, ich bin mir bewusst, wie veraltet MySQL 5.0 ist. Ich arbeite daran, die Datenbank auf MySQL 5.6 mit InnoDB (möglicherweise MariaDB 10 mit XtraDB) zu verschieben, aber bis dies geschieht, möchte ich dies mit der Datenbank so wie sie ist beantworten.

- -

Bearbeiten entfernt, siehe meine Antwort.

22
bafromca

Ich denke, wir haben die Antwort, die in meinem Fall erforderlich war , möglicherweise zu kompliziert. Ich habe keinen Zweifel daran, dass sowohl Roland als auch Rick James mit der Erstellung einer temporären Tabelle korrekt sind und nur Zeilen einfügen, die den Filter NOT LIKE '-%' Passieren, aber die Lösung für mich war "einfacher", weil ich einen wichtigen Fehler hatte bis jetzt nicht bewusst und dafür entschuldige ich mich.

Ich habe die Abfrage in der interaktiven Eingabeaufforderung mysql 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 wollte ich fand die Lösunginnodb_buffer_pool_size Über die Datei /etc/my.cnf Erhöhen und den MySQL-Daemon neu starten. Für meinen Server wurde der Standardwert 8M Festgelegt und ich habe ihn auf 1G Erhöht (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 ~ 27 Minuten löschen.

Für diejenigen, die neugierig sind, auf was innodb_buffer_pool_size Eingestellt werden soll, notieren Sie, wie viel RAM Sie haben) und dann werfen Sie einen Blick auf diesen Thread , der a ergibt vorgeschlagene Schätzung in GB.

21
bafromca

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

InnoDB Plumbing

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.coms Reasons for run-away main Innodb Tablespace :

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

In Ihrem Fall würde Grund Nr. 1 ein Rollback-Segment zusammen mit einem Teil des Rückgängig-Bereichs 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 diese Löschung jetzt beenden. Sobald Sie die Löschabfrage beenden, 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 gegen die MyISAM-Version der Tabelle tun können. Konvertieren Sie es dann in InnoDB.

24
RolandoMySQLDBA

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 macht, anstatt scheinbar ewig zu dauern: http://mysql.rjweb.org/doc.php/deletebig Der Kern ist, durch die zu gehen Tabelle über die PK, wobei 1K Zeilen gleichzeitig ausgeführt werden. (Natürlich gibt es weitere Details zu beachten.)

Und dieser Blog befasst sich mit potenziellen Fallstricken bei der Konvertierung in InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb

12
Rick James

Mein erster Instinkt 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
5
kristianp

Die einfachste Lösung besteht darin, dies einfach nicht zu tun - einen kleineren Löschvorgang durchzuführen, der einfacher verarbeitet werden kann.

In diesem Fall hätte ich empfohlen, das Formular nacheinander zu löschen:

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

Vielleicht könnten Sie so etwas tun:

  • Fügen Sie ein neues Feld mit dem Namen deleted hinzu.
  • Mach ein Update wie UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Stellen Sie cron ein, um dies nachts zu löschen.
2
Mike Minaev