Migrar una base de datos MySQL a PostgreSQL en empresa exige tres cosas que el ETL casero no entrega: mapeo riguroso de tipos de datos, validación fila a fila pre y post cutover, y un plan de rollback ejecutable en menos de quince minutos. La diferencia entre una migración limpia y una que rompe producción son las primeras dos semanas de discovery, no las herramientas. Aquí va el plan probado en proyectos reales con millones de filas.
¿Por qué migrar a PostgreSQL en 2026?
Las cinco razones que vemos en clientes empresariales LATAM.
- JSONB nativo: lógica de negocio con datos semiestructurados sin perder relacional
- Vistas materializadas con refresh concurrente, algo que MySQL maneja mal
- Row Level Security nativa, esencial para multi-tenant LATAM
- Extensiones poderosas: PostGIS para geo, pg_trgm para fuzzy search, pgvector para IA
- Licencia open source sin sustos enterprise como los de Oracle MySQL
Si tu empresa creció con MySQL Community y ahora maneja datos geoespaciales, IA o múltiples tenants, Postgres es el siguiente paso natural.
El mapeo de tipos: la fase que rompe más migraciones
| Tipo MySQL | Tipo PostgreSQL | Gotcha |
|---|---|---|
| TINYINT(1) | BOOLEAN | Cero a false, distinto de cero a true |
| DATETIME | TIMESTAMP o TIMESTAMPTZ | Decide zona horaria explícita |
| AUTO_INCREMENT | SERIAL o IDENTITY | Resetear secuencia tras carga |
| TEXT, MEDIUMTEXT, LONGTEXT | TEXT (uno solo) | Postgres no diferencia |
| ENUM | CREATE TYPE custom enum | Más estricto, valida más |
| DOUBLE | DOUBLE PRECISION | Casi igual, sin gotcha |
| GROUP_CONCAT | string_agg | Sintaxis diferente |
| ON UPDATE CURRENT_TIMESTAMP | Trigger explícito | Postgres no tiene atajo |
Cada uno de estos tipos tiene un caso donde la conversión silenciosa rompe lógica de negocio. El boolean es el más doloroso: un TINYINT(1) con valor 2 en MySQL es truthy; al migrar puede convertirse a true o a null según herramienta. Validación fila a fila lo cacha; auditoría agregada no.
El flujo de migración serio en siete fases
- Inventario completo: tablas, vistas, triggers, stored procedures, foreign keys
- Mapeo de tipos y de constraints, tabla por tabla, con tests unitarios
- Setup de Postgres destino con esquema idéntico antes de cargar datos
- Carga inicial con pgloader o ETL custom, tabla maestra a tabla detalle
- Validación fila a fila: conteo, suma de checksums, muestreo de 1,000 filas por tabla
- Replicación lógica para sync continuo durante ventana de cutover
- Cutover de aplicación con flag de feature, monitoreo activo, plan rollback documentado
Caso real: snapshot 98M filas en 48 horas
Trabajamos con una distribuidora multinacional en Guatemala con SQL Server origen (no MySQL, pero el principio aplica idéntico). Snapshot completo sin esquema destino: 239 países, 446 oficinas, 2.7 millones de clientes y contactos, histórico de 98 millones de filas.
- Snapshot worker Python con pymssql más PyArrow más Parquet
- Chunking de 8 paralelo, batch de 50,000, throttle de 10 queries por segundo
- 197 tablas snapshotteadas
- 2.7 GB de bronze parquet en Supabase Storage
- 2,528 archivos en bucket
- Verificación triple: source igual a bronze igual a silver
- Cero orphan FKs en población destino
- 204 oficinas reales operativas identificadas
Lo opuesto a un mysqldump nocturno y restaurar sobre Postgres rezando que no rompa nada.
Las cuatro estrategias de cutover
Según volumen y tolerancia a downtime, una de estas cuatro encaja con tu caso.
| Estrategia | Downtime | Volumen apto |
|---|---|---|
| Dump and restore | Horas a un día | Hasta 100GB, ventana fin de semana |
| ETL batch con freeze | 2 a 8 horas | 100GB a 1TB, ventana noche |
| Replicación lógica con switchover | Minutos | 1TB en adelante |
| Despliegue paralelo dual write | Cero | Crítico para banca, salud, retail 24/7 |
El despliegue paralelo es el estándar oro pero exige escribir capa de aplicación que mande writes a ambas bases temporalmente. Vale la pena para operaciones críticas.
Los siete errores que rompen producción
- No respaldar antes del cutover: el rollback se vuelve imposible si algo rompe
- Saltar la validación fila a fila: descubres discrepancias semanas después
- Cambiar collation sin probar: PostgreSQL maneja unicode distinto y orden cambia
- Ignorar zonas horarias: TIMESTAMP sin TZ pierde una hora dos veces al año
- Foreign keys sin orden de carga: violación de integridad y rollback masivo
- Sin plan de rollback: si la app cae, tienes treinta minutos antes que escalable a CEO
- Cero monitoreo post cutover: bugs silenciosos se descubren con cliente quejándose
¿Hacerlo interno o contratar?
Para volumen menor a 50GB y schema simple, un DBA interno con dos semanas dedicadas puede ejecutar bien. Para volumen mayor, integraciones con cinco o más sistemas, o requerimiento de cero downtime, la inversión externa se justifica.
Lo que antes tomaba treinta ingenieros y dieciocho meses, lo entregamos en doce semanas con metodología MAGIA Forge y arquitectura rigurosa. Sin retainers, sin licencias atadas, código a tu nombre.
Costos reales según volumen
| Volumen | Costo interno (2 DBAs 6 semanas) | Costo MAGIA Forge |
|---|---|---|
| Hasta 50GB, schema simple | 12,000 USD | 8,000 USD |
| 50 a 500GB, 5 sistemas | 60,000 USD | 20,000 USD único |
| Más de 1TB, mission critical | 150,000 USD | desde 20,000 USD con scope |
Más allá del precio, la diferencia clave es la documentación operativa. Una migración Catalizadora entrega runbook, plan de rollback y baseline de KPIs. Cero retainer post entrega.
Próximos pasos
Si tu MySQL ya no aguanta el volumen o necesitas extensiones modernas como JSONB, RLS multi-tenant o pgvector para IA, una sesión técnica gratis te ahorra meses de error costoso. MAGIA Forge entrega software a medida en doce semanas con CI/CD activo y pruebas automatizadas. MAGIA Core cubre cuando además necesitas data lake unificado y dashboards.
Llamada técnica con el equipo que construye, no con un SDR. Sin pitch deck.