Mysql
 sql >> Base de données >  >> RDS >> Mysql

L'insertion MySql dans la requête de sélection est trop lente pour copier 100 millions de lignes

N'importe quel INSERT ... SELECT ... la requête acquiert un verrou SHARED sur les lignes qu'il lit à partir de la table source dans le SELECT. Mais en traitant de plus petits morceaux de lignes, le verrou ne dure pas trop longtemps.

La requête avec LIMIT ... OFFSET va être de plus en plus lent à mesure que vous avancez dans la table source. À 10 000 lignes par bloc, vous devez exécuter cette requête 10 000 fois, chacune doit recommencer et parcourir la table pour atteindre le nouveau OFFSET.

Quoi que vous fassiez, la copie de 100 millions de lignes prendra un certain temps. Il fait beaucoup de travail.

J'utiliserais pt-archiver , un outil gratuit conçu à cet effet. Il traite les lignes en "morceaux" (ou sous-ensembles). Il ajustera dynamiquement la taille des morceaux afin que chaque morceau prenne 0,5 seconde.

La plus grande différence entre votre méthode et pt-archiver est que pt-archiver n'utilise pas LIMIT ... OFFSET , il parcourt l'index de clé primaire, en sélectionnant des morceaux de ligne par valeur plutôt que par position. Ainsi, chaque morceau est lu plus efficacement.

Concernant votre commentaire :

Je m'attends à ce que la réduction de la taille du lot - et l'augmentation du nombre d'itérations - rende le problème de performances empire , pas mieux.

La raison en est que lorsque vous utilisez LIMIT avec OFFSET , chaque requête doit recommencer au début de la table et compter les lignes jusqu'au OFFSET évaluer. Cela devient de plus en plus long au fur et à mesure que vous parcourez le tableau.

Exécution de 20 000 requêtes coûteuses à l'aide de OFFSET prendra plus de temps que d'exécuter 10 000 requêtes similaires. La partie la plus coûteuse ne sera pas la lecture de 5 000 ou 10 000 lignes, ni leur insertion dans la table de destination. La partie coûteuse sera de sauter environ 50 000 000 lignes, encore et encore.

Au lieu de cela, vous devez parcourir la table par valeurs pas par décalages.

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

Avant la boucle, interrogez MIN(id) et MAX(id) et démarrez rowOffset à la valeur min, et boucle jusqu'à la valeur max.

C'est ainsi que fonctionne pt-archiver.