Como converter uma tabela de 66.862.521 linhas de MyISAM para InnoDB sem ficar offline por várias horas?

18

é possível (e como) converter uma enorme tabela MyISAM no InnoDB sem colocar o aplicativo offline. É necessário inserir algumas linhas nessa tabela a cada segundo, mas é possível suspendê-la por cerca de 2 minutos.

Obviamente ALTER TABLE ... engine = innodb não funcionará. Portanto, eu tinha o plano de criar uma nova tabela com o mecanismo innodb e copiar o conteúdo para ela. E, no final, suspenda o encadeamento do log do aplicativo e RENAME TABLE.

Infelizmente, mesmo a cópia em pequenos lotes de 100 linhas gera um atraso significativo após algum tempo.

Editar : as linhas existentes nunca são alteradas; esta tabela é usada para o log.

Hendrik Brummermann
fonte
3
Bem, essa pergunta é sobre como minimizar o tempo de conversação. Não me importo se as conversas levarem alguns dias ou semanas. Mas deve funcionar em segundo plano sem exigir tempo de inatividade do aplicativo e sem criar um atraso perceptível.
Hendrik Brummermann

Respostas:

15

Crie uma configuração Master-Master da seguinte maneira:

  • Crie o segundo mestre, MasterB
  • MasterB atua como escravo de logTable
  • Criar logTable_newcomo innodb
  • Execute INSERT INTO logTable_new SELECT * FROM logTable(psuedocode) no MasterB, que envia a replicação para o MasterA
  • Quando o logTable_newMasterA terminar a sincronização, troque as tabelas
Derek Downey
fonte
10

Dada a restrição de:

Não me importo se as conversas levarem alguns dias ou semanas. Mas deve funcionar em segundo plano sem exigir tempo de inatividade do aplicativo e sem criar um atraso perceptível

Enquanto você faz o log, se você tem uma boa maneira de definir um marcador para saber o que inicia no processo, pode reaplicar todos os logs ou gravá-los em um arquivo de texto. mais tarde você pode ingeri-los com LOAD DATA INFILE

Parte do problema é que escrever em lotes menores significa que os índices precisam ser recalculados repetidamente; é melhor executar tudo de uma só vez, mas isso pode causar algum atraso 'perceptível' no sistema .. mas você não precisa fazer isso no servidor de produção.

  1. Pause o registro ou defina algum marcador para que você possa reaplicar os registros desse ponto em diante mais tarde.
  2. Copie sua tabela MyISM para outro sistema
  3. No outro sistema, crie uma tabela do InnoDB com um nome diferente e migre os dados (pode ser mais rápido despejá-lo e usá-lo LOAD DATA INFILE)
  4. Copie a tabela do InnoDB de volta ao sistema original
  5. Defina outro marcador para o log.
  6. Reaplique todos os logs na nova tabela entre os dois últimos marcadores.
  7. (repita as etapas 5 e 6 se a etapa 6 demorar mais ou menos um minuto, até que sejam apenas alguns segundos)
  8. Troque as tabelas (renomeie o antigo para table_BACKUP, o novo com o nome do antigo)
  9. Apanhe os logs desde o último marcador.
Joe
fonte
9

Infelizmente, mesmo a cópia em pequenos lotes de 100 linhas gera um atraso significativo após algum tempo.

Você está adicionando algum atraso entre cada lote ou apenas fazendo o lote das atualizações e executando cada lote diretamente após o anterior?

Nesse caso, tente criar um script da conversão no seu idioma favorito com algo como:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

Isso deve garantir que a conversão não ocupe mais ou menos a metade da capacidade do servidor, permitindo diferenças de carga impostas, pois o uso do sistema varia com o tempo.

Ou, se você desejar usar o máximo de tempo possível quando o serviço estiver relativamente inativo, mas recuar (potencialmente fazer uma pausa por um longo período de tempo) quando o banco de dados precisar fazer algum trabalho para seus usuários, substitua sleep for as long as the update tookpor if the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>. Isso significa que ele pode avançar em tempos de silêncio, mas será interrompido completamente quando o servidor estiver ocupado executando sua carga de trabalho normal. A determinação da carga dependerá do seu sistema operacional - no Linux e semelhante ao valor médio de carga de 1 minuto /proc/loadavgou a saída de uptimedeve fazer. <lower measure>e <upper measure>pode ter o mesmo valor, embora seja comum em controles como esse ter uma diferença, para que seu processo não continue e depois imediatamente para uma pausa, pois sua própria reinicialização influencia a medida de carga.

Obviamente, isso não funcionaria para tabelas nas quais as linhas antigas podem ser modificadas, mas funcionará bem para uma tabela de log como a que você descreve.

Você desejará ignorar a sabedoria usual de criar índices depois de preencher a nova tabela nesse caso. Embora isso seja realmente mais eficiente quando você deseja que as coisas sejam o mais rápido possível (o efeito no restante do sistema seja danificado), nesse caso, você não deseja o grande excesso de carga no final do processo como o os índices são completamente criados de uma só vez, pois esse é um processo que você não pode parar quando as coisas ficam ocupadas.

David Spillett
fonte
4

Algo assim funcionaria?

  1. Pausar o registro (para que a $auto_incrementtabela de registro mytable não seja alterada).
  2. Anote o $auto_incrementvalor usando SHOW TABLE STATUS LIKE 'mytable'.
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. Ative o log novamente. A tabela Innodb agora começará a ser preenchida.
  7. INSERT INTO mytable SELECT * FROM mytable_old.

Você pode executar a etapa 7 em lotes ou em uma instrução, pois ela não deve estar bloqueando o log normal.

Riedsio
fonte
ainda estaria bloqueando, por causa da maneira como o innodb lida com auto_increment. por padrão, o innodb usa um bloqueio no nível da tabela ao inserir em uma coluna de auto_increment e libera o bloqueio assim que a inserção é concluída.
Ovais.tariq