Migrar banco MySQL para MariaDB no Linux sem corromper tabelas é transferir schema, dados e rotinas por dump lógico para uma instância MariaDB 11.8 LTS limpa, no Ubuntu 24.04 LTS, sem reaproveitar o datadir do MySQL 8.4 LTS. Trocar só o pacote, ou copiar arquivos InnoDB com o servidor aberto, não é migração segura. Para migrar sem perda, siga estes passos:
- Pare as gravações, desative o Event Scheduler e inventarie bancos, engines, collations e objetos.
- Gere um dump lógico com transação consistente, rotinas, triggers e events.
- Verifique collations do MySQL 8 no dump; no MariaDB 11.8 são mapeadas automaticamente, mas versões anteriores ao 11.4.5 exigem ajuste manual.
- Importe o SQL em um MariaDB 11.8 vazio, com o cliente mariadb.
- Compare tabelas, contagem de linhas e hashes lógicos com a origem.
- Aponte a aplicação só depois do teste de leitura e escrita, mantendo a aplicação em manutenção até a validação concluir.
Pré-requisitos
A janela de manutenção precisa existir antes de qualquer comando: o dump marca um instante, e toda gravação feita depois dele fica de fora do arquivo. Reserve um servidor de destino com Ubuntu 24.04 LTS, disco livre para o SQL e para um datadir novo, e acesso administrativo ao MySQL 8.4 de origem. O destino deve ser MariaDB 11.8 LTS, não a série antiga que o repositório padrão do Ubuntu pode entregar. Confirme as duas versões antes de exportar. Se o cliente do destino não mostrar 11.8, pare e ajuste o repositório oficial do MariaDB; importar em outra série muda o teste de homologação.
- Origem: MySQL 8.4 LTS no Ubuntu 24.04 LTS, com usuário administrativo e o binário mysqldump dessa mesma instalação.
- Destino: MariaDB 11.8 LTS em outro host, ou em homologação com as mesmas versões da produção. Não instale os dois servidores apontando para o mesmo diretório de dados.
- Espaço em disco fora de /var/lib/mysql para o dump e uma cópia dele. Confira com df -h.
- Lista dos bancos da aplicação. Não entram mysql, sys, performance_schema nem information_schema.
- Acesso para parar a aplicação, workers e cron que gravam no banco. Firewall do destino restrito ao IP da aplicação, sem abrir a porta 3306 para qualquer origem.
- Homologação já feita com um dump anterior. A janela real usa um dump novo, tirado com as gravações paradas.
O caminho deste artigo não é o de levar o mesmo MySQL para outro Linux. Se origem e destino forem ambos MySQL, use o procedimento de Migrar banco de dados MySQL entre servidores Linux sem perda. Aqui o produto muda, então o datadir não viaja junto.
Migrar banco MySQL para MariaDB no Linux sem corromper tabelas
O dump lógico é o formato que reduz o risco de corromper tabelas, porque o MariaDB recria cada objeto em vez de abrir arquivos InnoDB escritos pelo MySQL 8.4. A partir do MySQL 8 o dicionário de dados é do próprio servidor. O MariaDB 11.8 não trata esse diretório como cópia compatível. Copiar /var/lib/mysql a quente, ou apontar o mariadbd para essa pasta depois de um apt install, pode impedir a subida do serviço ou gravar em páginas que o outro mecanismo não entende. Por isso a instância de destino nasce vazia, com mysql_install_db feito pelo pacote, e só recebe SQL.
Há diferença de produto, não só de nome de pacote. Autenticação, collation padrão e sql_mode não são intercambiáveis. O MySQL 8.4 cria contas com caching_sha2_password. O MariaDB não importa o schema mysql da origem. Recrie o usuário da aplicação no destino, com privilégio mínimo, e teste a conexão antes do corte. Para decidir se a troca de produto cabe na aplicação, leia Passo a passo: MariaDB é MySQL? escolha o banco certo antes de marcar a janela.
O ensaio em homologação usa as mesmas versões: mysqldump do MySQL 8.4 e cliente mariadb do 11.8. Corrija lá o erro de sintaxe, o DEFINER ausente e a collation. Repetir o ensaio não substitui o dump final. Na janela, pare as gravações, exporte de novo, importe e só então troque o host. Mantenha o MySQL 8.4 instalado e o datadir intocado até a validação passar. Desligar o serviço de origem antes de conferir a contagem é o atalho que transforma um rollback simples em restauração de emergência.
Inventariar engines, charset e objetos
A collation precisa ser conhecida antes do export, senão o erro aparece no meio da importação, com a janela já aberta. No servidor de origem, entre com o usuário administrativo e registre a versão, os bancos e o motor de cada tabela da aplicação. Troque appdb pelo nome real. Se houver mais de um banco, repita o filtro ou liste os nomes no dump com --databases.
mysql --user=root -p -e "SELECT VERSION();"
mysql --user=root -p -N -e "SHOW DATABASES;"
mysql --user=root -p -e "SELECT table_name, engine, table_collation FROM information_schema.tables WHERE table_schema='appdb' ORDER BY table_name;"
Ao rodar o primeiro comando, a tabela de resultado traz a função VERSION() e um valor que começa com 8.4. Se aparecer outra série, pare: o ensaio feito em versão diferente não vale para a produção. Anote toda linha cujo engine não seja InnoDB. O snapshot transacional não congela MyISAM. Nesse caso a aplicação tem de ficar sem gravação durante o dump inteiro, não só no instante em que o mysqldump abre a transação.
mysql --user=root -p -N -e "SELECT table_name, table_collation FROM information_schema.tables WHERE table_schema='appdb' AND table_collation LIKE '%0900%';"
mysql --user=root -p -e "SHOW FULL TABLES IN appdb WHERE TABLE_TYPE='VIEW';"
mysql --user=root -p -e "SHOW TRIGGERS FROM appdb;"
mysql --user=root -p -e "SHOW PROCEDURE STATUS WHERE Db='appdb';"
mysql --user=root -p -e "SHOW FUNCTION STATUS WHERE Db='appdb';"
mysql --user=root -p -e "SHOW EVENTS FROM appdb;"
Guarde também os DEFINER de views, triggers, procedures e events. Esse usuário terá de existir no MariaDB, com privilégio para executar o objeto, antes de a aplicação depender dele. Não copie a tabela mysql.user. Contas do MySQL 8.4 carregam plugin e colunas que o MariaDB não reaproveita. O checklist de objetos evita a surpresa de uma rotina que só aparece quando o cron roda no dia seguinte. Use o Checklist para Migração de Banco de Dados MySQL: diagnóstico e prevenção de erros como lista de conferência, sem misturar o método de cópia de arquivos com este dump.
Gerar o dump lógico com InnoDB consistente
InnoDB permite um dump consistente sem travar a leitura por horas, desde que o mysqldump seja o do MySQL 8.4 e a opção --single-transaction esteja presente. Isso não captura gravações feitas depois do início da transação. Por isso, na janela real, pare a aplicação, os workers e o cron antes de exportar. Em seguida verifique se não ficou consulta de escrita aberta e, se o inventário listou events, desative o Event Scheduler antes do dump: um evento habilitado pode executar depois da conferência do PROCESSLIST e as alterações não entrarão no arquivo.
Atenção: parar o serviço da aplicação interrompe o site ou a API. Avise a janela e só continue se o dump de ensaio já tiver importado em homologação.
# Verificar o Event Scheduler e desativá-lo se houver events que gravam
mysql --user=root -p -e "SELECT @@GLOBAL.event_scheduler;"
mysql --user=root -p -e "SET GLOBAL event_scheduler=OFF;"
# Aguardar eventos em execução terminarem antes de continuar
mysql --user=root -p -e "SHOW FULL PROCESSLIST;"
# Preparar diretório de trabalho acessível ao usuário corrente
sudo mkdir -p /var/backups/migracao
sudo chown "$(whoami):" /var/backups/migracao
mysqldump --user=root -p --single-transaction --routines --triggers --events --hex-blob --default-character-set=utf8mb4 --set-gtid-purged=OFF --databases appdb --result-file=/var/backups/migracao/dump-appdb.sql
O mysqldump bem-sucedido não imprime o SQL no terminal quando --result-file é usado. A senha é pedida no prompt; não a coloque na linha de comando. --set-gtid-purged=OFF evita gravar no arquivo um SET de GTID que o MariaDB recusa. --hex-blob protege colunas binárias. --routines, --triggers e --events incluem os objetos que um dump só de tabelas deixaria para trás. Não use --all-databases: esse atalho leva o schema mysql junto. O Event Scheduler deve ser reativado apenas no servidor que ficar em produção, após a validação; anote o estado original para não esquecer de religar no servidor correto.
Se existir tabela MyISAM, não confie só em --single-transaction. Pare as gravações e mantenha essa parada até o arquivo terminar. Ao final, a última linha útil do arquivo deve ser o comentário Dump completed. Sem essa linha, o processo foi interrompido: apague o arquivo incompleto e exporte de novo. Não importe um SQL cortado no meio de um INSERT.
tail -n 3 /var/backups/migracao/dump-appdb.sql
grep -c "CREATE TABLE" /var/backups/migracao/dump-appdb.sql
cp -a /var/backups/migracao/dump-appdb.sql /var/backups/migracao/dump-appdb.sql.orig
O grep devolve um inteiro aproximado: mysqldump pode emitir estruturas auxiliares de CREATE TABLE para views, então o número pode ser ligeiramente maior do que o total de tabelas base. Use-o como inspeção rápida de completude do arquivo, não como critério de integridade; a validação precisa por tabelas é feita via information_schema na etapa de conferência. Copie o arquivo para fora do servidor de origem antes de qualquer pacote ser removido. O dump é o backup da janela. Restaurar produção a partir dele, se a importação no destino falhar, segue a mesma disciplina descrita em Como Fazer Restore de Backup MariaDB Sem Corromper Produção: validar o arquivo antes, não por cima de uma instância que já recebeu escrita.
Ajustar o SQL e importar numa instância limpa
A instância limpa é um MariaDB 11.8 que nunca abriu o datadir do MySQL. No destino, confira a versão e só então importe. Se o Ubuntu 24.04 entregar outra série pelo apt padrão, não continue: o teste deixa de representar a produção. O arquivo de configuração do pacote Debian fica em /etc/mysql/mariadb.conf.d/50-server.cnf. Ajuste bind-address para o IP do próprio servidor de banco, por exemplo 203.0.113.20, e reinicie o serviço. Não use 0.0.0.0 se o firewall não estiver filtrando.
mariadb --version
mariadb --user=root -p -e "SELECT VERSION();"
sudo ss -lptn 'sport = :3306'
A linha de versão precisa citar MariaDB e 11.8. No ss, a escuta deve mostrar o IP escolhido e a porta 3306, com o processo mariadbd. Se o firewall ufw estiver ativo, libere só o IP da aplicação, 203.0.113.10 neste exemplo. Se o ufw estiver inativo, ele não lista regras e não protege a porta: mantenha o bind-address restrito e o filtro da rede. Não desative firewall como atalho.
sudo ufw status
sudo ufw allow from 203.0.113.10 to any port 3306 proto tcp
Antes da importação, use o grep abaixo como inventário de collations 0900 presentes no dump. No MariaDB 11.8, as collations utf8mb4_0900_* do MySQL 8 são mapeadas automaticamente para as equivalentes utf8mb4_uca1400_* (MDEV-20912); o dump importa sem erro de collation desconhecida nessa série. Se o destino for uma versão anterior ao MariaDB 11.4.5, o alias não existe: nesse caso mapeie cada collation 0900 encontrada para a variante utf8mb4_uca1400 correspondente — não para utf8mb4_unicode_ci, que usa UCA 4.0 e altera ordenação e unicidade de índices. Qualquer substituição deve ser testada em homologação antes da janela real.
grep -o 'utf8mb4_0900_[a-z0-9_]*' /var/backups/migracao/dump-appdb.sql | sort -u
Atenção: se uma substituição for necessária para versões antigas, opere sempre sobre o arquivo de trabalho, nunca sobre o .orig. A collation binária ou sensível a acento não deve virar unicode_ci sem teste de unicidade, senão um índice único pode passar a colidir ou aceitar duplicata.
Antes de importar, aumente o limite de pacotes do servidor para cobrir INSERTs grandes gerados por --hex-blob e --extended-insert. O limite do cliente isolado não resolve erros emitidos pelo servidor.
# No servidor MariaDB de destino, como root do banco
mariadb --user=root -p -e "SET GLOBAL max_allowed_packet=536870912;"
mariadb --user=root -p --max-allowed-packet=512M < /var/backups/migracao/dump-appdb.sql
O dump feito com --databases já contém CREATE DATABASE e USE. Não force a continuação com opção que ignora erro: um CREATE interrompido deixa tabela pela metade e a contagem mente. Crie o usuário da aplicação em sessão interativa com o histórico desabilitado, para evitar que a senha apareça no histórico do shell ou na lista de processos durante a execução. Troque a senha de exemplo e não conceda ALL PRIVILEGES nem use o plugin do MySQL 8.
# Abre sessão sem registrar histórico
HISTFILE=/dev/null mariadb --user=root -p
-- Execute dentro da sessão interativa:
CREATE USER 'app'@'203.0.113.10' IDENTIFIED BY 'troque-esta-senha';
GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON appdb.* TO 'app'@'203.0.113.10';
FLUSH PRIVILEGES;
Se houver event no inventário, o agendador do MariaDB pode estar desligado. Ligue só se a aplicação depender desses events e grave a opção na seção de servidor para sobreviver ao restart. Não copie sql_mode do MySQL 8.4 linha por linha. Rode a aplicação em homologação e ajuste o que o teste quebrar.
Conferir checksum e contagem antes do corte
A contagem exata é a prova de que a importação não perdeu linha. information_schema.tables.table_rows no InnoDB é estimativa: não use esse campo para liberar o corte. Conte as tabelas críticas na origem e no destino, com a aplicação ainda parada, e compare o texto. Os comandos com mysql devem ser executados no servidor de origem; os com mariadb, no servidor de destino. Transfira os arquivos de resultado para uma mesma máquina antes de rodar o diff.
# Executar no servidor de ORIGEM
mysql --user=root -p -N \
-e "SELECT table_name FROM information_schema.tables WHERE table_schema='appdb' AND table_type='BASE TABLE' ORDER BY table_name;" \
> tabelas-origem.txt
# Executar no servidor de DESTINO
mariadb --user=root -p -N \
-e "SELECT table_name FROM information_schema.tables WHERE table_schema='appdb' AND table_type='BASE TABLE' ORDER BY table_name;" \
> tabelas-destino.txt
# Comparar após transferir ambos os arquivos para a mesma máquina
diff tabelas-origem.txt tabelas-destino.txt
Diff sem nenhuma linha significa listas iguais. Qualquer nome só de um lado é tabela faltando ou sobrando: não troque o host da aplicação. Para cada tabela crítica, rode a mesma contagem e um hash lógico nos dois servidores. O CHECKSUM TABLE não é portátil entre MySQL e MariaDB: o algoritmo interno difere entre os produtos, versões e formatos de linha, e o valor pode divergir mesmo com dados idênticos, gerando falso positivo e abortando uma migração correta. Use hashes lógicos calculados sobre as mesmas colunas com serialização definida.
# Executar no servidor de ORIGEM
mysql --user=root -p \
-e "SELECT COUNT(*) AS total, BIT_XOR(CRC32(CONCAT_WS('#',id,status,updated_at))) AS hash FROM appdb.pedidos;"
# Executar no servidor de DESTINO
mariadb --user=root -p \
-e "SELECT COUNT(*) AS total, BIT_XOR(CRC32(CONCAT_WS('#',id,status,updated_at))) AS hash FROM appdb.pedidos;"
Substitua id, status e updated_at pelas colunas representativas da tabela, incluindo a chave primária. A coluna total e o hash têm de ser idênticos nos dois lados. Em tabelas muito grandes, divida a consulta em intervalos de chave primária. Não compare hashes calculados com colunas diferentes nos dois lados.
Liste de novo views, triggers, procedures, functions e events no MariaDB. Abra uma sessão com o usuário app a partir do IP 203.0.113.10 e faça um teste de leitura e de escrita que a aplicação realmente usa: insert em tabela filha, update e rollback ou delete do registro de teste. Um SELECT isolado não prova GRANT, trigger nem procedure. Mantenha a aplicação em modo de manutenção até esse teste passar: enquanto nenhuma gravação real entrar no MariaDB, o datadir do MySQL permanece íntegro como rollback. Se o MariaDB já recebeu gravações e for necessário retornar ao MySQL, interrompa novas escritas, reconcilie os registros inseridos ou alterados no MariaDB e só então retorne. Mantenha o MySQL 8.4 instalado e o serviço parado durante todo o período de validação. Só depois de tudo conferido altere o host no arquivo de configuração da aplicação para 203.0.113.20, suba o serviço e acompanhe o log de conexão.
Problemas comuns e como resolver
Sintoma: a importação para em Unknown collation utf8mb4_0900_ai_ci
Causa: o destino é uma série de MariaDB anterior ao 11.4.5, que não inclui os aliases para collations utf8mb4_0900_* do MySQL 8. No MariaDB 11.8 esse erro não ocorre: as collations 0900 são mapeadas automaticamente para as equivalentes utf8mb4_uca1400_*.
Solução: use MariaDB 11.8 ou posterior. Se precisar usar uma série anterior ao 11.4.5, interrompa o uso dessa instância se o erro ocorreu no meio do arquivo, apague o banco importado pela metade ou recrie o destino vazio, restaure o SQL a partir de dump-appdb.sql.orig e mapeie cada collation 0900 encontrada pelo grep para a variante utf8mb4_uca1400 correspondente — não para utf8mb4_unicode_ci, que usa UCA 4.0 diferente. Importe de novo sem ignorar erro e confira a contagem outra vez: uma importação interrompida não é uma migração parcial aceitável.
Sintoma: a view ou a procedure falha com definidor inexistente
Causa: o objeto foi criado com DEFINER de um usuário que existia no MySQL e não foi recriado no MariaDB, ou o host do usuário não é o mesmo.
Solução: crie o usuário de definição no destino, no host certo, com privilégio suficiente para manter o objeto. Não importe mysql.user para resolver isso. Se o ensaio mostrar que o definidor não pode existir, gere um dump novo e trate o DEFINER em homologação antes da janela. Depois liste SHOW TRIGGERS, SHOW PROCEDURE STATUS e as views, e repita o teste de escrita da aplicação.
Sintoma: o MariaDB não sobe ao usar o datadir do MySQL 8.4
Causa: o diretório /var/lib/mysql da origem foi copiado ou reaproveitado. Os arquivos InnoDB e o dicionário do MySQL 8.4 não são a base de uma instalação MariaDB 11.8.
Solução: pare o MariaDB e não aponte de novo para essa pasta. Renomeie o diretório copiado para um nome de quarentena, deixe o pacote criar um datadir vazio e importe o dump lógico. Não use recuperação forçada de InnoDB como método de migração. Se o único arquivo bom for o dump, restaure por ele em instância limpa. O MySQL de origem, se ainda estiver intacto, continua sendo a fonte até a validação.
Perguntas frequentes
Dá para migrar MySQL para MariaDB só trocando o pacote no Linux?
Não é seguro tratar a troca de pacote como migração completa. Pare as gravações, faça dump lógico consistente e importe em uma instância MariaDB limpa, conferindo charset, contagem de linhas e integridade antes de desligar o MySQL.
Qual formato de dump reduz o risco de corromper tabelas?
Use dump lógico com transação consistente em tabelas InnoDB, rotinas e eventos incluídos, e importe com o cliente do MariaDB. Evite copiar o datadir a quente: arquivos InnoDB copiados com o servidor aberto podem ficar inconsistentes.
Views, triggers e procedures sobrevivem à migração?
Em geral sim, se o dump incluir rotinas, triggers e events e o usuário de definição existir no destino com os mesmos privilégios. Depois da importação, liste esses objetos e execute um teste de leitura e escrita da aplicação, não só um SELECT em uma tabela.
Como saber se houve perda de dados depois da importação?
Compare a lista de bancos e tabelas, a contagem de linhas das tabelas críticas e hashes lógicos calculados sobre as mesmas colunas antes e depois. Só troque o host da aplicação quando esses números baterem e um teste funcional passar no MariaDB.
MariaDB 11.8 lê um dump gerado no MySQL 8.4?
Um dump lógico em SQL costuma importar, mas autenticação, sql_mode e alguns tipos ou funções podem exigir ajuste. Teste a importação em homologação com as mesmas versões de produção e corrija erros de sintaxe antes da janela real.
Conclusão
O corte de tráfego só acontece depois da prova, não depois do apt install. O MySQL 8.4 permanece disponível até o MariaDB 11.8 mostrar as mesmas tabelas, as mesmas contagens e um teste real da aplicação. DEFINER ausente e schema de sistema são pontos que mais quebram uma importação que parecia simples; collations 0900 são resolvidas automaticamente no MariaDB 11.8, mas exigem mapeamento manual em séries anteriores ao 11.4.5.
- Ensaie o dump do MySQL 8.4 no MariaDB 11.8 em homologação e corrija definidores e eventuais incompatibilidades antes da janela.
- Na janela, pare as gravações, desative o Event Scheduler, exporte de novo com --single-transaction e importe em instância vazia, sem copiar o datadir.
- Compare listas e COUNT(*) com hashes lógicos; mantenha a aplicação em manutenção até o teste funcional passar, e só então aponte para o MariaDB.
Leia também
- Como otimizar o MySQL para alta performance em VPS Linux: guia completo
- Checklist para Migração de Banco de Dados MySQL: diagnóstico e prevenção de erros
- Como otimizar o MySQL para alta performance no VPS Linux (com exemplos reais)
Precisa de ajuda com a migração do banco?
Uma janela curta e um dump testado evitam transformar incompatibilidade de versão em tabela inconsistente. Se quiser revisar o plano antes de parar a aplicação, fale com quem opera esse tipo de migração no dia a dia.