Enhver INSERT ... SELECT ...
forespørgsel skaffer en DELT lås
på rækkerne den læser fra kildetabellen i SELECT. Men ved at behandle mindre bidder af rækker holder låsen ikke for længe.
Forespørgslen med LIMIT ... OFFSET
vil være langsommere og langsommere, efterhånden som du går gennem kildetabellen. Ved 10.000 rækker pr. chunk skal du køre den forespørgsel 10.000 gange, hver enkelt skal starte forfra og scanne gennem tabellen for at nå den nye OFFSET.
Uanset hvad du gør, vil kopiering af 100 millioner rækker tage et stykke tid. Det gør en masse arbejde.
Jeg ville bruge pt-archiver , et gratis værktøj designet til dette formål. Den behandler rækkerne i "bidder" (eller delmængder). Det vil dynamisk justere størrelsen af bidderne, så hver chunk tager 0,5 sekunder.
Den største forskel mellem din metode og pt-archiver er, at pt-archiver ikke bruger LIMIT ... OFFSET
, går den langs det primære nøgleindeks og vælger bidder af række efter værdi i stedet for efter position. Så hver del læses mere effektivt.
Om din kommentar:
Jeg forventer, at at gøre batchstørrelsen mindre – og øge antallet af iterationer – vil gøre ydeevneproblemet værre , ikke bedre.
Årsagen er, at når du bruger LIMIT
med OFFSET
, hver forespørgsel skal starte forfra ved begyndelsen af tabellen og tælle rækkerne op til OFFSET
værdi. Dette bliver længere og længere, efterhånden som du gentager bordet.
Kører 20.000 dyre forespørgsler ved hjælp af OFFSET
vil tage længere tid end at køre 10.000 lignende forespørgsler. Den dyreste del vil ikke være at læse 5.000 eller 10.000 rækker eller indsætte dem i destinationstabellen. Den dyre del vil være at springe gennem ~50.000.000 rækker igen og igen.
I stedet bør du iterere over tabellen efter værdier ikke ved forskydninger.
INSERT IGNORE INTO Table2(id, field2, field3)
SELECT f1, f2, f3
FROM Table1
WHERE id BETWEEN rowOffset AND rowOffset+limitSize;
Før løkken, forespørg MIN(id) og MAX(id), og start rowOffset
ved min-værdien, og sløjfe op til max-værdien.
Dette er måden pt-archiver fungerer på.