Mysql
 sql >> Datenbank >  >> RDS >> Mysql

MySQL-Einfügung in ausgewählte Abfrage ist zu langsam, um 100 Millionen Zeilen zu kopieren

Beliebiges INSERT ... SELECT ... -Abfrage erwirbt eine SHARED-Sperre in den Zeilen, die aus der Quelltabelle in SELECT gelesen werden. Aber durch die Verarbeitung kleinerer Zeilenblöcke dauert die Sperre nicht allzu lange.

Die Abfrage mit LIMIT ... OFFSET wird langsamer und langsamer, je weiter Sie durch die Quelltabelle vorrücken. Bei 10.000 Zeilen pro Block müssen Sie diese Abfrage 10.000 Mal ausführen, jeder muss von vorne beginnen und die Tabelle durchsuchen, um den neuen OFFSET zu erreichen.

Egal, was Sie tun, das Kopieren von 100 Millionen Zeilen wird eine Weile dauern. Es macht eine Menge Arbeit.

Ich würde pt-archiver verwenden , ein kostenloses Tool, das für diesen Zweck entwickelt wurde. Es verarbeitet die Zeilen in "Blöcken" (oder Teilmengen). Die Größe der Chunks wird dynamisch angepasst, sodass jeder Chunk 0,5 Sekunden dauert.

Der größte Unterschied zwischen Ihrer Methode und pt-archiver besteht darin, dass pt-archiver LIMIT ... OFFSET nicht verwendet , geht es den Primärschlüsselindex entlang und wählt Zeilenabschnitte nach Wert statt nach Position aus. So wird jeder Chunk effizienter gelesen.

Zu Ihrem Kommentar:

Ich gehe davon aus, dass eine Verkleinerung der Stapelgröße – und eine Erhöhung der Anzahl der Iterationen – das Leistungsproblem schlimmer machen wird , nicht besser.

Der Grund dafür ist, dass Sie LIMIT verwenden mit OFFSET , muss jede Abfrage am Anfang der Tabelle beginnen und die Zeilen bis zum OFFSET zählen Wert. Dies wird immer länger, wenn Sie die Tabelle durchlaufen.

Ausführen von 20.000 teuren Abfragen mit OFFSET dauert länger als die Ausführung von 10.000 ähnlichen Abfragen. Der teuerste Teil besteht nicht darin, 5.000 oder 10.000 Zeilen zu lesen oder sie in die Zieltabelle einzufügen. Der teure Teil besteht darin, immer und immer wieder ~50.000.000 Zeilen zu überspringen.

Stattdessen sollten Sie die Tabelle nach Werten durchlaufen nicht durch Offsets.

INSERT IGNORE INTO Table2(id, field2, field3)
        SELECT f1, f2, f3
        FROM Table1
        WHERE id BETWEEN rowOffset AND rowOffset+limitSize;

Fragen Sie vor der Schleife MIN(id) und MAX(id) ab und starten Sie rowOffset beim Min-Wert und Schleife bis zum Max-Wert.

So funktioniert pt-archiver.