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

15 KiB

title slug summary client industry timeline role image tags featured order date seo_title seo_description seo_keywords
Migración CNPJ Alfanumérico - 100 Millones de Registros cnpj-migration-database Ejecución de migración masiva de CNPJ numérico a alfanumérico en base de datos con ~100M registros, usando estrategia de commits faseados para evitar bloqueo. Empresa de Cobranza Cobranza & Servicios Financieros En ejecución Database Architect & Tech Lead
SQL Server
Database Migration
CNPJ
Performance Optimization
Batch Processing
Big Data
true 4 2024-11-01 Migración CNPJ Alfanumérico - 100M Registros | Carneiro Tech Caso de migración masiva de CNPJ en base de datos con 100 millones de registros usando commits faseados y optimizaciones de performance. database migration, SQL Server, CNPJ, batch processing, performance optimization, phased commits

Descripción General

Una empresa de cobranza que trabaja con bases de datos de información transitoria (sin software propietario) necesita adaptar sus sistemas al nuevo formato de CNPJ alfanumérico brasileño.

Desafío principal: Migrar ~100 millones de registros en tablas con columnas BIGINT y NUMERIC a VARCHAR, sin bloquear la base de datos en producción.

Estado: Proyecto en ejecución (preparación de scripts de migración).


Desafío

Volumen Masivo de Datos

Contexto de la empresa:

  • Empresa de cobranza (no desarrolla software propio)
  • Trabaja con datos transitorios (alta rotación)
  • Base de datos SQL Server con volumen crítico

Análisis inicial reveló:

Tabla Columna Tipo Actual Registros Tamaño
Deudores CNPJ_Deudor BIGINT 8.000.000 60 GB
Transacciones 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. 🔴 Tablas con 8M+ líneas usando BIGINT para CNPJ
  2. 🔴 90 millones de registros en tabla de transacciones
  3. 🔑 CNPJ como clave primaria en algunas tablas
  4. 🔗 Foreign keys relacionando múltiples tablas
  5. ⚠️ Imposibilidad de downtime prolongado (operación 24/7)
  6. 💾 Restricciones de espacio en disco (necesita estrategia eficiente)

Decisión Estratégica: Phased Commits

¿Por qué NO hacer ALTER COLUMN directo?

Enfoque ingenuo (NO funciona):

-- ❌ NUNCA HAGA ESTO EN TABLAS GRANDES
ALTER TABLE Transacciones
ALTER COLUMN CNPJ_Pagador VARCHAR(18);

Problemas:

  • 🔒 Bloquea la tabla entera durante la conversión
  • ⏱️ Puede tomar horas/días en tablas grandes
  • 💥 Bloquea todas las operaciones (INSERT, UPDATE, SELECT)
  • 🚨 Riesgo de timeout o falla en medio de la operación
  • 🔙 Rollback complejo si algo sale mal

Estrategia Elegida: Column Swap con Commits Faseados

Basado en experiencia anterior, decidí usar enfoque gradual:

┌─────────────────────────────────────────────┐
│  1. Crear nueva columna VARCHAR al FINAL    │
│     (operación rápida, no bloquea tabla)    │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  2. UPDATE en lotes (commits faseados)      │
│     - 100k registros a la vez               │
│     - Pausa entre lotes (evita contención)  │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  3. Remover PKs y FKs                       │
│     (tras 100% migrado)                     │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  4. Renombrar columnas (swap)               │
│     - CNPJ → CNPJ_Old                       │
│     - CNPJ_New → CNPJ                       │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  5. Recrear PKs/FKs con nueva columna       │
└─────────────────────────────────────────────┘
                    ▼
┌─────────────────────────────────────────────┐
│  6. Validación y eliminación columna vieja  │
└─────────────────────────────────────────────┘

¿Por qué este enfoque?

Sin lock de tabla completa (operación incremental) Puede pausar/reanudar en cualquier momento Monitoreo de progreso en tiempo real Rollback simple (basta eliminar nueva columna) Minimiza impacto en producción (commits pequeños)

Decisión tomada basada en:

  • 📚 Experiencia anterior con migraciones de gran volumen
  • 🔍 Conocimiento de locks de SQL Server
  • 🎯 Necesidad de zero downtime

Nota: Esta decisión fue tomada sin consultar IA - basada puramente en experiencia práctica de proyectos anteriores.


Detalles de Implementación

Fase 1: Crear Nueva Columna

-- Operación rápida (metadata change solamente)
ALTER TABLE Transacciones
ADD CNPJ_Pagador_New VARCHAR(18) NULL;

-- Agrega índice temporal para acelerar lookups
CREATE NONCLUSTERED INDEX IX_Temp_CNPJ_New
ON Transacciones(CNPJ_Pagador_New)
WHERE CNPJ_Pagador_New IS NULL;

Tiempo estimado: ~1 segundo (independiente del tamaño de la tabla)


Fase 2: Migración en Lotes (Core Strategy)

-- Script de migración con 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;

    -- Actualiza lote de 100k registros aún no migrados
    UPDATE TOP (@BatchSize) Transacciones
    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 progreso
    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 (reduce contención)
    WAITFOR DELAY '00:00:01';  -- 1 segundo entre lotes
END;

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

Parámetros configurables:

  • @BatchSize: 100k (balanceado entre performance y lock time)
    • Muy pequeño = muchas transacciones, overhead
    • Muy grande = lock prolongado, impacto en prod
  • WAITFOR DELAY: 1 segundo (da tiempo a otras queries para ejecutar)

Estimaciones de tiempo:

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

Ventajas:

  • No bloquea aplicación
  • Otras queries pueden ejecutar entre los lotes
  • Puede pausar (Ctrl+C) y reanudar después (WHERE NULL toma desde donde paró)
  • Log de progreso en tiempo real

Fase 3: Remoción de Constraints

-- Identifica todas las PKs y FKs que involucran la columna
SELECT name
FROM sys.key_constraints
WHERE type = 'PK'
    AND parent_object_id = OBJECT_ID('Transacciones')
    AND COL_NAME(parent_object_id, parent_column_id) = 'CNPJ_Pagador';

-- Remueve PKs
ALTER TABLE Transacciones
DROP CONSTRAINT PK_Transacciones_CNPJ;

-- Remueve FKs (tablas que referencian)
ALTER TABLE Pagos
DROP CONSTRAINT FK_Pagos_Transacciones;

Tiempo estimado: ~10 minutos (depende de cuántas constraints existen)


Fase 4: Column Swap (Renombramiento)

-- Renombra columna antigua a _Old
EXEC sp_rename 'Transacciones.CNPJ_Pagador', 'CNPJ_Pagador_Old', 'COLUMN';

-- Renombra nueva columna al nombre original
EXEC sp_rename 'Transacciones.CNPJ_Pagador_New', 'CNPJ_Pagador', 'COLUMN';

-- Altera a NOT NULL (tras validación de 100% completado)
ALTER TABLE Transacciones
ALTER COLUMN CNPJ_Pagador VARCHAR(18) NOT NULL;

Tiempo estimado: ~1 segundo (metadata change)


Fase 5: Recreación de Constraints

-- Recrea PK con nueva columna VARCHAR
ALTER TABLE Transacciones
ADD CONSTRAINT PK_Transacciones_CNPJ
PRIMARY KEY CLUSTERED (CNPJ_Pagador);

-- Recrea FKs
ALTER TABLE Pagos
ADD CONSTRAINT FK_Pagos_Transacciones
FOREIGN KEY (CNPJ_Pagador) REFERENCES Transacciones(CNPJ_Pagador);

Tiempo estimado: ~30-60 minutos (depende del volumen)


Fase 6: Validación y Limpieza

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

-- Valida integridad referencial
DBCC CHECKCONSTRAINTS WITH ALL_CONSTRAINTS;

-- Si todo OK, remueve columna antigua
ALTER TABLE Transacciones
DROP COLUMN CNPJ_Pagador_Old;

-- Remueve índice temporal
DROP INDEX IX_Temp_CNPJ_New ON Transacciones;

Personalización del Proceso CNPJ Fast

Diferencias vs. Proceso Original

El proceso CNPJ Fast original fue reestructurado para este cliente:

Cambios principales:

Aspecto CNPJ Fast Original Cliente (Personalizado)
Foco Aplicaciones + DB Solo DB (sin software propio)
Discovery Inventario de apps Solo análisis de schema
Ejecución Múltiples aplicaciones Scripts SQL masivos
Batch Size 50k-100k 100k (optimizado para volumen)
Monitoreo Manual + herramientas Logs SQL en tiempo real
Rollback Proceso complejo Simple (DROP COLUMN)

Motivo de la reestructuración:

  • Cliente no tiene aplicaciones propias (solo consume datos)
  • Foco 100% en optimización de base de datos
  • Volumen mucho mayor 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


Decisiones Clave & Trade-offs

¿Por qué 100k por batch?

Pruebas de performance:

Batch Size Tiempo/Batch Lock Duration Contención
10.000 2s Bajo Mínimo
50.000 8s Medio Aceptable
100.000 15s Medio Balanceado
500.000 90s Alto Impacto en prod
1.000.000 180s Muy alto Inaceptable

Elección: 100k ofrece mejor balance entre performance e impacto.


¿Por qué crear columna al FINAL?

Internals de SQL Server:

  • Agregar columna al final = metadata change (rápido)
  • Agregar en medio = reescritura de páginas (lento)
  • Para tablas grandes, posición importa

¿Por qué WAITFOR DELAY de 1 segundo?

Sin delay:

  • Batch processing consume 100% del I/O
  • Queries de aplicación se vuelven lentas
  • Lock escalation puede ocurrir

Con delay de 1s:

  • Otras queries tienen ventana para ejecutar
  • I/O distribuido
  • Experiencia del usuario preservada

Trade-off: Migración toma +1s por batch (~25% más lenta), pero sistema permanece responsivo.


Estado Actual & Próximos Pasos

Estado Actual (Diciembre 2024)

📝 Fase de Preparación:

  • Discovery completo (100M registros identificados)
  • Scripts de migración desarrollados
  • Pruebas en ambiente de homologación
  • 🔄 Validación de performance
  • Esperando ventana de mantenimiento para producción

Próximos Pasos

  1. Backup completo de producción
  2. Ejecución en producción (ambiente 24/7)
  3. Monitoreo en tiempo real durante migración
  4. Validación post-migración (integridad, performance)
  5. Documentación de lessons learned

Lecciones Aprendidas (Hasta Ahora)

1. Experiencia Anterior Vale Oro

Decisión de usar phased commits vino de experiencia práctica en proyectos anteriores, no de documentación o IA.

Situaciones similares anteriores:

  • Migración de datos en e-commerce (50M registros)
  • Conversión de encoding (UTF-8 en 100M+ rows)
  • Particionamiento de tablas históricas

2. "Measure Twice, Cut Once"

Antes de ejecutar en producción:

  • Pruebas exhaustivas en homologación
  • Scripts validados y revisados
  • Rollback probado
  • Estimaciones de tiempo confirmadas

Tiempo de preparación: 3 semanas Tiempo de ejecución: Estimado en 48 horas

Ratio: 10:1 (preparación vs ejecución)


3. Personalización > One-Size-Fits-All

El proceso CNPJ Fast original necesitó ser reestructurado para este cliente.

Lección: Los procesos deben ser:

  • Estructurados lo suficiente para repetir
  • Flexibles lo suficiente para adaptar

4. Monitoreo es Crucial

Scripts con log detallado de progreso permiten:

  • Estimar tiempo restante
  • Identificar cuellos de botella
  • Pausar/reanudar con confianza
  • Reportar estado a stakeholders
-- Log example
Processed: 10.000.000 rows. Batch: 100.000
Elapsed time: 3600 seconds (10% complete, ~9h remaining)

Optimizaciones de Performance

Optimizaciones Implementadas

  1. Índice temporal WHERE NULL

    • Acelera lookup de registros no migrados
    • Removido tras conclusión
  2. Batch size optimizado

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

    -- Verificar crecimiento del log
    DBCC SQLPERF(LOGSPACE);
    
    -- Ajustar recovery model (si permitido)
    ALTER DATABASE MyDatabase SET RECOVERY SIMPLE;
    
  4. Ejecución en horario de menor carga

    • Ventana de mantenimiento nocturna
    • Fin de semana (si es posible)

Resultado esperado: Migración de 100 millones de registros en ~48 horas, sin downtime significativo y con posibilidad de rollback rápido.

¿Necesita migrar volúmenes masivos de datos? Póngase en contacto