ALTER TABLE sem downtime no MariaDB: mudando tabelas gigantes com o sistema no ar

Adicionar uma coluna não deveria exigir janela de manutenção de madrugada. No MariaDB, na maioria dos casos, não exige.
Resumo para quem tem pressa: um ALTER TABLE mal planejado numa tabela de centenas de milhões de linhas pode travar o sistema por horas. O InnoDB oferece quatro algoritmos: INSTANT, NOCOPY, INPLACE e COPY. ALGORITHM limita o trabalho estrutural permitido; LOCK controla a concorrência durante a operação. INSTANT evita modificar arquivos de dados, NOCOPY evita reconstruir o índice clusterizado, INPLACE pode reconstruir a tabela e COPY cria outra tabela e copia os dados. Desde o 11.2, o COPY aceita LOCK=NONE nas operações e engines compatíveis. Mesmo operações online precisam adquirir metadata locks e podem esperar por transações abertas. Os dois segredos são: sempre declarar ALGORITHM e LOCK no comando, para o banco recusar em vez de travar, e cuidar do metadata lock, o vilão que derruba sistemas mesmo em alterações "instantâneas".
Por que ALTER TABLE assusta tanto
Todo DBA tem uma história assim. Alguém precisa adicionar uma coluna observacao na tabela de pedidos. Roda o ALTER às 15h. A tabela tem 400 milhões de linhas, o banco começa a copiar tudo para uma estrutura nova, as gravações ficam esperando, as conexões se acumulam e o sistema cai. A "coluninha" virou incidente.
Dois mecanismos causam isso:
- Reconstrução da tabela: algumas alterações exigem reescrever todos os dados. Em tabelas grandes, isso leva horas.
- Bloqueio de metadados (metadata lock): mesmo uma alteração rápida precisa, por um instante, de acesso exclusivo à definição da tabela. Se ela tiver que esperar, todo mundo que chegar depois também espera.
O MariaDB resolve muito bem o primeiro ponto. O segundo depende de você, e eu mostro como mais abaixo.
Os algoritmos: do instantâneo ao pesado
| Algoritmo | O que faz | Tempo em tabela grande | Bloqueia escrita? |
|---|---|---|---|
INSTANT |
Evita modificar arquivos de dados | Normalmente rápido; pode esperar por locks | Ainda precisa de metadata locks |
NOCOPY |
Evita reconstruir o índice clusterizado (ex.: índice secundário) | Depende da operação e dos dados | Depende da operação e de LOCK |
INPLACE |
Pode reconstruir por dentro do InnoDB | Minutos a horas | Depende da operação e de LOCK |
COPY |
Cria uma tabela nova e copia tudo | Depende do volume e do hardware | Desde o 11.2, aceita LOCK=NONE nas combinações suportadas |
Se você não disser nada, o MariaDB escolhe o algoritmo mais leve possível. Parece ótimo, mas tem uma armadilha: se a alteração não puder ser instantânea, ele cai silenciosamente para um algoritmo mais pesado. Você acha que vai levar 1 segundo e leva 3 horas.
A regra de ouro: declare o que você espera
Sempre escreva o algoritmo e o tipo de bloqueio que você aceita. Se o MariaDB não conseguir cumprir, ele recusa com erro em vez de executar do jeito pesado:
-- Só executa se for instantâneo; caso contrário, dá erro e não faz nada
ALTER TABLE pedidos
ADD COLUMN observacao VARCHAR(500) NULL,
ALGORITHM=INSTANT;
-- Aceita reconstruir, mas sem bloquear leituras e escritas
ALTER TABLE pedidos
ADD INDEX idx_cliente_data (cliente_id, criado_em),
ALGORITHM=INPLACE, LOCK=NONE;
Exceto por COPY, o algoritmo declarado é o menos eficiente que você aceita: INPLACE também permite NOCOPY ou INSTANT. Esse hábito evita executar uma operação mais pesada do que a planejada. Se der erro, você para e planeja. O erro é barato; a surpresa em produção, não.
O que já é instantâneo
O InnoDB do MariaDB vem acumulando alterações instantâneas há várias versões:
- Adicionar coluna: instantâneo desde o 10.3 (no fim da tabela) e, desde o 10.4, em qualquer posição.
- Remover coluna e reordenar colunas: instantâneos desde o 10.4.
- Renomear coluna, mudar o valor
DEFAULT, mudar comentário: só metadados. - Aumentar um
VARCHAR: instantâneo em vários casos, dependendo do formato da linha e do tamanho em bytes.
-- Podem usar INSTANT nas versões e condições suportadas; ainda há metadata locks
ALTER TABLE pedidos ADD COLUMN canal VARCHAR(20) DEFAULT 'site' AFTER status, ALGORITHM=INSTANT;
ALTER TABLE pedidos DROP COLUMN campo_legado, ALGORITHM=INSTANT;
ALTER TABLE pedidos RENAME COLUMN obs TO observacao, ALGORITHM=INSTANT;
ALTER TABLE pedidos ALTER COLUMN canal SET DEFAULT 'app', ALGORITHM=INSTANT;
Atenção: há exceções, como tabelas com ROW_FORMAT=COMPRESSED e alguns casos com índice FULLTEXT. É exatamente por isso que você declara ALGORITHM=INSTANT: se não der, o MariaDB avisa antes de começar.
Índices: online desde sempre (quase)
Criar um índice secundário comum no InnoDB pode evitar a reconstrução do índice clusterizado e permitir escritas concorrentes. Há exceções, como operações envolvendo FULLTEXT ou SPATIAL; LOCK=NONE pede que o banco recuse quando não puder cumprir:
ALTER TABLE pedidos
ADD INDEX idx_status_criado (status, criado_em),
ALGORITHM=NOCOPY, LOCK=NONE;
Vai levar o tempo de ler a tabela e montar o índice, mas a aplicação continua funcionando. Para remover um índice com segurança, use o recurso de índices ignorados, que mostro no artigo MariaDB lento? Como achar e corrigir as consultas que estão derrubando seu sistema.
O grande salto do 11.2: até o COPY ficou online
Algumas alterações exigem reescrever todos os dados. O exemplo mais dramático é a chave primária INT que está chegando ao limite de 2,1 bilhões e precisa virar BIGINT. Até o 11.1, isso significava travar a tabela durante toda a cópia, ou recorrer a ferramentas externas.
No MariaDB 11.2, o algoritmo COPY passou a aceitar LOCK=NONE. Enquanto copia, o MariaDB registra em arquivo temporário as alterações que a aplicação continua fazendo e aplica tudo no final. Ainda há metadata locks na preparação e na troca final, sem duração garantida. Planeje espaço para a nova tabela e para o arquivo temporário de alterações concorrentes, e monitore o filesystem temporário.
-- MariaDB 11.2+, InnoDB: valide as restrições de AUTO_INCREMENT e FKs primeiro
ALTER TABLE eventos
MODIFY id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
ALGORITHM=COPY, LOCK=NONE;
A partir do 11.2, esse comportamento online é o padrão nas combinações suportadas; o InnoDB continua preferindo algoritmos nativos mais eficientes quando disponíveis. Se por algum motivo você quiser a cópia antiga com bloqueio, use LOCK=SHARED.
Limitações que valem conhecer (confira na documentação da sua versão):
- Não permite adicionar coluna nova
AUTO_INCREMENTou comDEFAULT (...NEXTVAL(...)). - Converter coluna existente para
AUTO_INCREMENTexige condições adicionais: originalmenteNOT NULL,NO_AUTO_VALUE_ON_ZEROdesativado e uma chave originalUNIQUE NOT NULLque permaneça inalterada. Por isso, valide também o exemplo deBIGINTacima. - Restrições de chave estrangeira com
CASCADE,SET NULLouSET DEFAULTimpedem COPY online; não se limitam aON DELETE. ALTER IGNORE,ALTER TABLE ... ORDER BYeDROP SYSTEM VERSIONINGnão são suportados nesse modo.- Versionamento baseado em transações exige
INPLACE/INSTANT. - As alterações concorrentes precisam respeitar as constraints do esquema final quando reaplicadas.
- No InnoDB, DML concorrente inclui
INSERT,UPDATEeDELETE. Em Aria/MyISAM, a documentação descreve inserções concorrentes, masUPDATEeDELETEficam bloqueados durante o ALTER. Não generalize para outras engines, como ColumnStore, CONNECT ou S3.
Atenção: se outras tabelas têm chave estrangeira apontando para o id que você vai transformar em BIGINT, as colunas que fazem referência também precisam mudar de tipo. Teste a sequência completa em homologação antes.
Se você ainda está em 10.x, esse é um dos argumentos mais concretos para migrar. Veja para qual versão ir agora que o 10.6 chegou ao fim de vida.
O vilão escondido: o metadata lock
Imagine que você roda um ALTER ... ALGORITHM=INSTANT, que deveria levar 50 milissegundos. Só que há uma transação aberta há 20 minutos usando a tabela, como um relatório pesado ou uma conexão que fez BEGIN e esqueceu do COMMIT. O ALTER precisa esperar essa transação terminar.
E agora vem o efeito dominó: toda consulta que chegar depois do ALTER fica na fila atrás dele, inclusive os SELECT simples. Em segundos, o pool de conexões esgota e o sistema para. No PROCESSLIST, você vê dezenas de linhas com Waiting for table metadata lock.
A referência atual documenta lock_wait_timeout=86400 segundos, um dia. Confira o valor efetivo na sua versão e configuração. O limite vale por tentativa de aquisição de metadata lock, não como duração máxima do ALTER. Três defesas:
1. Antes de alterar, procure transações longas:
-- Transações abertas, da mais antiga para a mais nova
SELECT trx_mysql_thread_id AS conexao,
trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS segundos_aberta,
LEFT(trx_query, 80) AS consulta
FROM information_schema.INNODB_TRX
ORDER BY trx_started;
2. Limite a espera do ALTER com WAIT ou NOWAIT, um recurso do MariaDB:
-- Desiste se não conseguir o bloqueio em 5 segundos (ainda pode formar fila nesse intervalo)
ALTER TABLE pedidos WAIT 5
ADD COLUMN canal VARCHAR(20) DEFAULT 'site', ALGORITHM=INSTANT;
-- Ou desiste na hora, se o lock necessário não puder ser adquirido imediatamente
ALTER TABLE pedidos NOWAIT
ADD COLUMN canal VARCHAR(20) DEFAULT 'site', ALGORITHM=INSTANT;
Se falhar, é só tentar de novo alguns segundos depois. É muito melhor do que derrubar a aplicação.
3. Ou ajuste o timeout só na sua sessão:
SET SESSION lock_wait_timeout = 5;
Acompanhe o progresso
Em alterações demoradas, o MariaDB informa o andamento, coisa que muita gente nem sabe que existe:
SELECT id, stage, max_stage, progress, LEFT(info, 60) AS comando
FROM information_schema.PROCESSLIST
WHERE info LIKE 'ALTER%';
A coluna progress mostra a porcentagem da etapa atual. Isso basta para responder "falta muito?" com dados, e não no chute.
E as réplicas?
Numa replicação tradicional, a réplica recebe o evento do ALTER concluído e precisa executar seu próprio trabalho; isso pode aumentar o atraso, conforme as gravações e a capacidade de cada servidor.
Desde o 10.8.1, existe uma alternativa em duas fases:
-- Na mesma sessão do primário que executará o ALTER:
SET SESSION binlog_alter_two_phase = ON;
O recurso é desativado por padrão e registra início e confirmação ou cancelamento separadamente. Use apenas com réplicas compatíveis com esses eventos e replicação paralela habilitada (slave_parallel_threads > 0, sem desabilitá-la para a conexão). A réplica pode começar ao receber o evento inicial: isso pode reduzir o atraso, mas não garante início simultâneo nem ausência de lag. SET GLOBAL muda o padrão de novas sessões, não o valor da sessão já conectada.
Particionamento: a alteração mais rápida é a que não acontece
Tabelas de log, eventos e histórico costumam ser as maiores do banco, e o pior que se faz com elas é um DELETE de milhões de linhas para limpar dados antigos. Com particionamento por data, remover uma partição pode evitar um DELETE linha a linha, mas ainda exige locks:
CREATE TABLE logs_acesso (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
criado_em DATETIME NOT NULL,
usuario INT,
rota VARCHAR(200),
PRIMARY KEY (id, criado_em)
)
PARTITION BY RANGE COLUMNS (criado_em) (
PARTITION p2026_09 VALUES LESS THAN ('2026-10-01'),
PARTITION p2026_10 VALUES LESS THAN ('2026-11-01'),
PARTITION p2026_11 VALUES LESS THAN ('2026-12-01')
);
-- Arquivar um mês: vira uma tabela comum, separada (MariaDB 10.7+)
ALTER TABLE logs_acesso CONVERT PARTITION p2026_09 TO TABLE logs_2026_09;
-- Ou descartar de vez (operação destrutiva: confirme os limites da partição)
-- ALTER TABLE logs_acesso DROP PARTITION p2026_09;
-- Criar a partição do próximo mês
ALTER TABLE logs_acesso ADD PARTITION
(PARTITION p2026_12 VALUES LESS THAN ('2027-01-01'));
O CONVERT PARTITION ... TO TABLE e o caminho inverso, CONVERT TABLE ... TO PARTITION, existem no MariaDB desde o 10.7 e podem simplificar o arquivamento, mas continuam sujeitos a locks. No sentido inverso, é obrigatório definir os limites da nova partição; WITH VALIDATION e WITHOUT VALIDATION existem desde o 11.4. Cuidado com o exemplo: a primeira partição recebe todos os valores anteriores a 2026-10-01, não apenas setembro; não descarte histórico sem conferir o limite inferior.
E o pt-online-schema-change?
Ferramentas como o pt-online-schema-change foram, por muitos anos, a única forma segura de alterar tabelas grandes no ecossistema MySQL. Elas criam uma tabela nova, copiam os dados aos poucos e usam triggers para acompanhar as mudanças.
Continuam úteis quando você precisa controlar o ritmo da cópia, por exemplo pausando quando a réplica atrasa. Mas, no MariaDB 11.2 ou superior, a maioria dos casos se resolve com o próprio ALTER TABLE, sem triggers de sincronização nem ferramenta externa; ALGORITHM=COPY ainda cria uma nova tabela interna.
O que isso significa para você
Checklist para qualquer ALTER em produção:
- Declare sempre
ALGORITHMeLOCK. Se der erro, replaneje. - Antes de rodar, procure transações longas no
INNODB_TRX. - Use
WAIT 5ouNOWAITpara não criar fila de metadata lock. - Em alterações longas, acompanhe o progresso pelo
PROCESSLIST. - Com réplicas compatíveis e replicação paralela, avalie e teste
binlog_alter_two_phase(10.8.1+). - Em tabelas de log e histórico, particione por data e troque
DELETEporDROPouCONVERT PARTITION. - Tenha um backup testado antes de qualquer alteração estrutural. Mostro como no artigo sobre backup sem parar o banco com mariadb-backup.
Leia também
-
MariaDB lento? Como achar e corrigir as consultas que estão derrubando seu sistema
-
Backup no MariaDB sem parar o banco: mariadb-backup, incremental e restauração até o minuto exato
-
MariaDB: tabelas temporais (System Versioning) para viajar no tempo dos dados
-
MariaDB Spider: sharding transparente com tabelas distribuídas
-
Migrar do MySQL para o MariaDB: o guia que eu queria ter lido antes
Todos os artigos sobre MariaDB →
Documentação oficial consultada
- Visão geral do InnoDB Online DDL
- Online Schema Change e limitações do COPY
- ALTER TABLE: WAIT e CONVERT PARTITION
- Variáveis de replicação e binlog
- Variáveis de sistema: lock_wait_timeout
Nota técnica: os exemplos foram revisados com a documentação, mas não executados em um servidor MariaDB nesta publicação. Valide versão, caminhos, privilégios, restrições e comportamento em laboratório antes de usar em produção. As capas são imagens fotográficas geradas por IA, não resultados de testes.
Me acompanhe no LinkedIn e no Instagram. Para fortalecer sua base em SQL, conheça meu livro Aprendendo MySQL. Conheça também a HTI Tecnologia.