Aprenda / SQL e banco de dados

IntermediárioAula 13 de 1617 min0% da trilha

Migrations seguras em produção

O mesmo ALTER TABLE que roda instantâneo no seu banco de teste pode travar uma tabela real por minutos.

Numa tabela de teste com 200 linhas, ALTER TABLE usuarios ADD COLUMN documento VARCHAR(20) NOT NULL roda em milissegundos. Na mesma tabela em produção, com 5 milhões de linhas e tráfego real acontecendo ao mesmo tempo, o mesmo comando pode segurar um lock que trava leituras e escritas por minutos — e minutos de indisponibilidade em produção custam caro.

Por que NOT NULL numa tabela grande é arriscado

Adicionar uma coluna nova, sozinha e sem obrigatoriedade, costuma ser rápido. O problema aparece quando você exige que toda linha já existente respeite uma regra nova — como NOT NULL — o banco precisa varrer a tabela inteira, linha por linha, pra confirmar que nenhuma viola a regra antes de aceitar a constraint. Numa tabela pequena isso é instantâneo; numa tabela de milhões de linhas, é uma varredura completa segurando um lock enquanto roda.

Estratégia em etapas — a forma segura

etapa 1 — coluna nova, ainda opcional
ALTER TABLE usuarios ADD COLUMN documento VARCHAR(20);
-- sem NOT NULL: essa etapa é rápida mesmo numa tabela grande
etapa 2 — backfill em lotes, não tudo de uma vez
UPDATE usuarios
SET documento = 'PENDENTE'
WHERE documento IS NULL AND id BETWEEN 1 AND 50000;
-- repete por faixas de id até cobrir a tabela inteira — em produção,
-- isso roda em background, em pedaços pequenos, nunca numa transação
-- gigante que trava a tabela do início ao fim
etapa 3 — só depois que toda linha já tem valor
ALTER TABLE usuarios ALTER COLUMN documento SET NOT NULL;
-- agora essa validação é rápida: já não existe nenhuma linha com
-- documento vazio pra encontrar no meio da varredura

Cada etapa é pequena e reversível separadamente — se algo der errado no backfill, dá pra pausar sem afetar a aplicação, já que a coluna ainda é opcional. Uma migration "tudo de uma vez" (criar a coluna já NOT NULL) não te dá esse meio-termo: ou funciona de primeira, ou trava a tabela tentando.

NOT VALID / VALIDATE CONSTRAINT — a mesma ideia pra CHECK

ALTER TABLE usuarios
  ADD CONSTRAINT documento_nao_vazio CHECK (documento <> '') NOT VALID;
-- a constraint já vale pra todo INSERT/UPDATE novo a partir de agora,
-- sem varrer o dado existente nesse momento

ALTER TABLE usuarios VALIDATE CONSTRAINT documento_nao_vazio;
-- essa segunda etapa varre o dado já existente, mas com um lock mais
-- leve do que adicionar a constraint validada de uma vez só

Pensando em rollback

Migrations em etapas pequenas facilitam reverter: tirar um NOT NULL recém-aplicado é rápido; apagar uma coluna nullable que ainda não é usada por nenhum código não quebra nada. O risco cresce quando uma migration remove ou renomeia algo que o código em produção ainda depende — o código antigo (rodando enquanto o deploy novo ainda não terminou) não pode quebrar por causa da migration mais nova.

Exercício

Você precisa tornar obrigatória (NOT NULL) a coluna telefone numa tabela usuarios com 5 milhões de linhas, em produção, sem causar indisponibilidade. Escreva o SQL essencial de cada etapa.

Quiz rápido

Por que um ALTER TABLE que adiciona uma constraint NOT NULL numa tabela de milhões de linhas pode ser perigoso em produção?
Qual a estratégia mais segura pra tornar uma coluna nova obrigatória (NOT NULL) numa tabela grande já em produção?