Qualquer
INSERT ... SELECT ...
consulta adquire um bloqueio COMPARTILHADO
nas linhas que ele lê da tabela de origem no SELECT. Mas ao processar pedaços menores de linhas, o bloqueio não dura muito. A consulta com
LIMIT ... OFFSET
será cada vez mais lento à medida que você avança na tabela de origem. Com 10.000 linhas por bloco, você precisa executar essa consulta 10.000 vezes, cada uma deve começar de novo e varrer a tabela para chegar ao novo OFFSET. Não importa o que você faça, copiar 100 milhões de linhas vai demorar um pouco. Está dando muito trabalho.
Eu usaria pt-archiver , uma ferramenta gratuita desenvolvida para esse fim. Ele processa as linhas em "pedaços" (ou subconjuntos). Ele ajustará dinamicamente o tamanho dos pedaços para que cada pedaço leve 0,5 segundos.
A maior diferença entre o seu método e o pt-archiver é que o pt-archiver não usa
LIMIT ... OFFSET
, ele percorre o índice de chave primária, selecionando pedaços de linha por valor em vez de por posição. Assim, cada pedaço é lido com mais eficiência. Re seu comentário:
Espero que diminuir o tamanho do lote — e aumentar o número de iterações — torne o problema de desempenho pior , não é melhor.
A razão é que quando você usa
LIMIT
com OFFSET
, cada consulta deve recomeçar no início da tabela e contar as linhas até o OFFSET
valor. Isso fica cada vez mais longo à medida que você percorre a tabela. Executando 20.000 consultas caras usando
OFFSET
levará mais tempo do que executar 10.000 consultas semelhantes. A parte mais cara não será ler 5.000 ou 10.000 linhas ou inseri-las na tabela de destino. A parte cara será pular cerca de 50.000.000 de linhas, repetidamente. Em vez disso, você deve iterar sobre a tabela por valores não por compensações.
INSERT IGNORE INTO Table2(id, field2, field3)
SELECT f1, f2, f3
FROM Table1
WHERE id BETWEEN rowOffset AND rowOffset+limitSize;
Antes do loop, consulte MIN(id) e MAX(id) e inicie
rowOffset
no valor mínimo e faça um loop até o valor máximo. É assim que o pt-archiver funciona.