Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

A inserção do MySql na consulta selecionada é muito lenta para copiar 100 milhões de linhas


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.