Mysql
 sql >> Database >  >> RDS >> Mysql

L'inserimento di MySql nella query di selezione è troppo lento per copiare 100 milioni di righe

Qualsiasi INSERT ... SELECT ... query acquisisce un blocco CONDIVISO sulle righe legge dalla tabella di origine in SELECT. Ma elaborando blocchi di righe più piccoli, il blocco non dura troppo a lungo.

La query con LIMIT ... OFFSET sarà sempre più lento man mano che avanzi nella tabella di origine. A 10.000 righe per blocco, devi eseguire quella query 10.000 volte, ognuna deve ricominciare da capo ed eseguire la scansione della tabella per raggiungere il nuovo OFFSET.

Qualunque cosa tu faccia, copiare 100 milioni di righe richiederà del tempo. Sta facendo molto lavoro.

Userei pt-archiver , uno strumento gratuito progettato per questo scopo. Elabora le righe in "blocchi" (o sottoinsiemi). Regolerà dinamicamente la dimensione dei blocchi in modo che ogni blocco richieda 0,5 secondi.

La più grande differenza tra il tuo metodo e pt-archiver è che pt-archiver non usa LIMIT ... OFFSET , cammina lungo l'indice della chiave primaria, selezionando blocchi di riga per valore anziché per posizione. Quindi ogni pezzo viene letto in modo più efficiente.

Re il tuo commento:

Mi aspetto che ridurre le dimensioni del batch e aumentare il numero di iterazioni renderà il problema delle prestazioni peggiore , non meglio.

Il motivo è che quando usi LIMIT con OFFSET , ogni query deve ricominciare dall'inizio della tabella e contare le righe fino a OFFSET valore. Questo diventa sempre più lungo man mano che scorri la tabella.

Esecuzione di 20.000 query costose utilizzando OFFSET richiederà più tempo rispetto all'esecuzione di 10.000 query simili. La parte più costosa non sarà leggere 5.000 o 10.000 righe o inserirle nella tabella di destinazione. La parte più costosa salterà attraverso circa 50.000.000 di righe, più e più volte.

Invece, dovresti scorrere la tabella per valori non per offset.

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

Prima del ciclo, interroga MIN(id) e MAX(id) e avvia rowOffset al valore minimo e torna al valore massimo.

Questo è il modo in cui funziona pt-archiver.