CarneiroTech/Content/Cases/pt/cnpj-migration-database.md

15 KiB

title slug summary client industry timeline role image tags featured order date seo_title seo_description seo_keywords
Migração CNPJ Alfanumérico - 100 Milhões de Registros cnpj-migration-database Execução de migração massiva de CNPJ numérico para alfanumérico em banco de dados com ~100M registros, usando estratégia de commits faseados para evitar travamento. Empresa de Cobrança Cobrança & Serviços Financeiros Em execução Database Architect & Tech Lead
SQL Server
Database Migration
CNPJ
Performance Optimization
Batch Processing
Big Data
true 4 2024-11-01 Migração CNPJ Alfanumérico - 100M Registros | Carneiro Tech Case de migração massiva de CNPJ em banco de dados com 100 milhões de registros usando commits faseados e otimizações de performance. database migration, SQL Server, CNPJ, batch processing, performance optimization, phased commits

Overview

Uma empresa de cobrança que trabalha com bancos de dados de informação transitória (sem software proprietário) precisa adaptar seus sistemas ao novo formato de CNPJ alfanumérico brasileiro.

Desafio principal: Migrar ~100 milhões de registros em tabelas com colunas BIGINT e NUMERIC para VARCHAR, sem travar o banco de dados em produção.

Status: Projeto em execução (preparação de scripts de migração).


Challenge

Volume Massivo de Dados

Contexto da empresa:

  • Empresa de cobrança (não desenvolve software próprio)
  • Trabalha com dados transitórios (alta rotatividade)
  • Banco de dados SQL Server com volume crítico

Análise inicial revelou:

Tabela Coluna Tipo Atual Registros Tamanho
Devedores CNPJ_Devedor BIGINT 8.000.000 60 GB
Transações CNPJ_Pagador NUMERIC(14) 90.000.000 1.2 TB
Empresas CNPJ_Empresa BIGINT 2.500.000 18 GB
TOTAL - - ~100.000.000 ~1.3 TB

Problemas identificados:

  1. 🔴 Tabelas com 8M+ linhas usando BIGINT para CNPJ
  2. 🔴 90 milhões de registros em tabela de transações
  3. 🔑 CNPJ como chave primária em algumas tabelas
  4. 🔗 Foreign keys relacionando múltiplas tabelas
  5. ⚠️ Impossibilidade de downtime prolongado (operação 24/7)
  6. 💾 Restrições de espaço em disco (precisa estratégia eficiente)

Strategic Decision: Phased Commits

Por que NÃO fazer ALTER COLUMN direto?

Abordagem ingênua (NÃO funciona):

-- ❌ NUNCA FAÇA ISSO EM TABELAS GRANDES
ALTER TABLE Transacoes
ALTER COLUMN CNPJ_Pagador VARCHAR(18);

Problemas:

  • 🔒 Trava a tabela inteira durante a conversão
  • ⏱️ Pode levar horas/dias em tabelas grandes
  • 💥 Bloqueia todas as operações (INSERT, UPDATE, SELECT)
  • 🚨 Risco de timeout ou falha no meio da operação
  • 🔙 Rollback complexo se algo der errado

Estratégia Escolhida: Column Swap com Commits Faseados

Baseado em experiência anterior, decidi usar abordagem gradual:

┌─────────────────────────────────────────────┐
│  1. Criar nova coluna VARCHAR no FINAL      │
│     (operação rápida, não bloqueia tabela)  │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  2. UPDATE em lotes (commits faseados)      │
│     - 100k registros por vez                │
│     - Pausa entre lotes (evita contenção)   │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  3. Remover PKs e FKs                       │
│     (após 100% migrado)                     │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  4. Renomear colunas (swap)                 │
│     - CNPJ → CNPJ_Old                       │
│     - CNPJ_New → CNPJ                       │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  5. Recriar PKs/FKs com nova coluna         │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  6. Validação e exclusão da coluna antiga   │
└─────────────────────────────────────────────┘

Por que essa abordagem?

Sem lock de tabela completa (operação incremental) Pode pausar/retomar a qualquer momento Monitoramento de progresso em tempo real Rollback simples (basta dropar nova coluna) Minimiza impacto em produção (commits pequenos)

Decisão tomada baseada em:

  • 📚 Experiência anterior com migrações de grande volume
  • 🔍 Conhecimento de locks do SQL Server
  • 🎯 Necessidade de zero downtime

Nota: Essa decisão foi tomada sem consultar IA - baseada puramente em experiência prática de projetos anteriores.


Implementation Details

Fase 1: Criar Nova Coluna

-- Operação rápida (metadata change apenas)
ALTER TABLE Transacoes
ADD CNPJ_Pagador_New VARCHAR(18) NULL;

-- Adiciona índice temporário para acelerar lookups
CREATE NONCLUSTERED INDEX IX_Temp_CNPJ_New
ON Transacoes(CNPJ_Pagador_New)
WHERE CNPJ_Pagador_New IS NULL;

Tempo estimado: ~1 segundo (independente do tamanho da tabela)


Fase 2: Migração em Lotes (Core Strategy)

-- Script de migração com commits faseados
DECLARE @BatchSize INT = 100000;  -- 100k registros por lote
DECLARE @RowsAffected INT = 1;
DECLARE @TotalProcessed INT = 0;
DECLARE @StartTime DATETIME = GETDATE();

WHILE @RowsAffected > 0
BEGIN
    BEGIN TRANSACTION;

    -- Atualiza lote de 100k registros ainda não migrados
    UPDATE TOP (@BatchSize) Transacoes
    SET CNPJ_Pagador_New = RIGHT('00000000000000' + CAST(CNPJ_Pagador AS VARCHAR), 14)
    WHERE CNPJ_Pagador_New IS NULL;

    SET @RowsAffected = @@ROWCOUNT;
    SET @TotalProcessed = @TotalProcessed + @RowsAffected;

    COMMIT TRANSACTION;

    -- Log de progresso
    PRINT 'Processed: ' + CAST(@TotalProcessed AS VARCHAR) + ' rows. Batch: ' + CAST(@RowsAffected AS VARCHAR);
    PRINT 'Elapsed time: ' + CAST(DATEDIFF(SECOND, @StartTime, GETDATE()) AS VARCHAR) + ' seconds';

    -- Pausa entre lotes (reduz contenção)
    WAITFOR DELAY '00:00:01';  -- 1 segundo entre lotes
END;

PRINT 'Migration completed! Total rows: ' + CAST(@TotalProcessed AS VARCHAR);

Parâmetros configuráveis:

  • @BatchSize: 100k (balanceado entre performance e lock time)
    • Muito pequeno = muitas transações, overhead
    • Muito grande = lock prolongado, impacto em prod
  • WAITFOR DELAY: 1 segundo (dá tempo de outras queries rodarem)

Estimativas de tempo:

Registros Batch Size Tempo Estimado
8.000.000 100.000 ~2-3 horas
90.000.000 100.000 ~20-24 horas

Vantagens:

  • Não trava aplicação
  • Outras queries conseguem rodar entre os lotes
  • Pode pausar (Ctrl+C) e retomar depois (WHERE NULL pega de onde parou)
  • Log de progresso em tempo real

Fase 3: Remoção de Constraints

-- Identifica todas as PKs e FKs envolvendo a coluna
SELECT name
FROM sys.key_constraints
WHERE type = 'PK'
    AND parent_object_id = OBJECT_ID('Transacoes')
    AND COL_NAME(parent_object_id, parent_column_id) = 'CNPJ_Pagador';

-- Remove PKs
ALTER TABLE Transacoes
DROP CONSTRAINT PK_Transacoes_CNPJ;

-- Remove FKs (tabelas que referenciam)
ALTER TABLE Pagamentos
DROP CONSTRAINT FK_Pagamentos_Transacoes;

Tempo estimado: ~10 minutos (depende de quantas constraints existem)


Fase 4: Column Swap (Renomeação)

-- Renomeia coluna antiga para _Old
EXEC sp_rename 'Transacoes.CNPJ_Pagador', 'CNPJ_Pagador_Old', 'COLUMN';

-- Renomeia nova coluna para o nome original
EXEC sp_rename 'Transacoes.CNPJ_Pagador_New', 'CNPJ_Pagador', 'COLUMN';

-- Altera para NOT NULL (após validação de 100% preenchido)
ALTER TABLE Transacoes
ALTER COLUMN CNPJ_Pagador VARCHAR(18) NOT NULL;

Tempo estimado: ~1 segundo (metadata change)


Fase 5: Recriação de Constraints

-- Recria PK com nova coluna VARCHAR
ALTER TABLE Transacoes
ADD CONSTRAINT PK_Transacoes_CNPJ
PRIMARY KEY CLUSTERED (CNPJ_Pagador);

-- Recria FKs
ALTER TABLE Pagamentos
ADD CONSTRAINT FK_Pagamentos_Transacoes
FOREIGN KEY (CNPJ_Pagador) REFERENCES Transacoes(CNPJ_Pagador);

Tempo estimado: ~30-60 minutos (depende do volume)


Fase 6: Validação e Limpeza

-- Valida que 100% foi migrado
SELECT COUNT(*)
FROM Transacoes
WHERE CNPJ_Pagador IS NULL OR CNPJ_Pagador = '';

-- Valida integridade referencial
DBCC CHECKCONSTRAINTS WITH ALL_CONSTRAINTS;

-- Se tudo OK, remove coluna antiga
ALTER TABLE Transacoes
DROP COLUMN CNPJ_Pagador_Old;

-- Remove índice temporário
DROP INDEX IX_Temp_CNPJ_New ON Transacoes;

Customização do Processo CNPJ Fast

Diferenças vs. Processo Original

O processo CNPJ Fast original foi reestruturado para este cliente:

Mudanças principais:

Aspecto CNPJ Fast Original Cliente (Customizado)
Foco Aplicações + DB Apenas DB (sem software próprio)
Discovery Inventário de apps Apenas análise de schema
Execução Múltiplas aplicações Scripts SQL massivos
Batch Size 50k-100k 100k (otimizado para volume)
Monitoramento Manual + ferramentas Logs SQL em tempo real
Rollback Processo complexo Simples (DROP COLUMN)

Motivo da reestruturação:

  • Cliente não tem aplicações próprias (apenas consome dados)
  • Foco 100% em otimização de banco
  • Volume muito maior que casos típicos (100M vs ~10M)

Tech Stack

SQL Server T-SQL Batch Processing Performance Tuning Database Optimization Migration Scripts Phased Commits Index Optimization Constraint Management


Key Decisions & Trade-offs

Por que 100k por batch?

Testes de performance:

Batch Size Tempo/Batch Lock Duration Contenção
10.000 2s Baixo Mínimo
50.000 8s Médio Aceitável
100.000 15s Médio Balanceado
500.000 90s Alto Impacto em prod
1.000.000 180s Muito alto Inaceitável

Escolha: 100k oferece melhor balanço entre performance e impacto.


Por que criar coluna no FINAL?

SQL Server internals:

  • Adicionar coluna no final = metadata change (rápido)
  • Adicionar no meio = reescrita de páginas (lento)
  • Para tabelas grandes, posição importa

Por que WAITFOR DELAY de 1 segundo?

Sem delay:

  • Batch processing consome 100% do I/O
  • Queries de aplicação ficam lentas
  • Lock escalation pode ocorrer

Com delay de 1s:

  • Outras queries têm janela para executar
  • I/O distribuído
  • Experiência do usuário preservada

Trade-off: Migração leva +1s por batch (~25% mais lenta), mas sistema permanece responsivo.


Current Status & Next Steps

Status Atual (Dezembro 2024)

📝 Fase de Preparação:

  • Discovery completo (100M registros identificados)
  • Scripts de migração desenvolvidos
  • Testes em ambiente de homologação
  • 🔄 Validação de performance
  • Aguardando janela de manutenção para produção

Próximos Passos

  1. Backup completo de produção
  2. Execução em produção (ambiente 24/7)
  3. Monitoramento em tempo real durante migração
  4. Validação pós-migração (integridade, performance)
  5. Documentação de lessons learned

Lessons Learned (Até Agora)

1. Experiência Anterior Vale Ouro

Decisão de usar phased commits veio de experiência prática em projetos anteriores, não de documentação ou IA.

Situações similares anteriores:

  • Migração de dados em e-commerce (50M registros)
  • Conversão de encoding (UTF-8 em 100M+ rows)
  • Particionamento de tabelas históricas

2. "Measure Twice, Cut Once"

Antes de executar em produção:

  • Testes exaustivos em homologação
  • Scripts validados e revisados
  • Rollback testado
  • Estimativas de tempo confirmadas

Tempo de preparação: 3 semanas Tempo de execução: Estimado em 48 horas

Ratio: 10:1 (preparação vs execução)


3. Customização > One-Size-Fits-All

O processo CNPJ Fast original precisou ser reestruturado para este cliente.

Lição: Processos devem ser:

  • Estruturados o suficiente para repetir
  • Flexíveis o suficiente para adaptar

4. Monitoramento é Crucial

Scripts com log detalhado de progresso permitem:

  • Estimar tempo restante
  • Identificar gargalos
  • Pausar/retomar com confiança
  • Reportar status para stakeholders
-- Log example
Processed: 10.000.000 rows. Batch: 100.000
Elapsed time: 3600 seconds (10% complete, ~9h remaining)

Performance Optimizations

Otimizações Implementadas

  1. Índice temporário WHERE NULL

    • Acelera lookup de registros não migrados
    • Removido após conclusão
  2. Batch size otimizado

    • Balanceado entre performance e lock time
  3. Transaction log management

    -- Verificar crescimento do log
    DBCC SQLPERF(LOGSPACE);
    
    -- Ajustar recovery model (se permitido)
    ALTER DATABASE MyDatabase SET RECOVERY SIMPLE;
    
  4. Execução em horário de menor carga

    • Janela de manutenção noturna
    • Final de semana (se possível)

Resultado esperado: Migração de 100 milhões de registros em ~48 horas, sem downtime significativo e com possibilidade de rollback rápido.

Precisa migrar volumes massivos de dados? Entre em contato