Tutoriales 10 min de lectura

Cómo migrar de MySQL a PostgreSQL sin perder datos

Guía completa para planificar y ejecutar la migración de una base de datos MySQL a PostgreSQL con pgloader, incluyendo conversión de tipos, ajuste de queries y validación de integridad.

Terminal mostrando el proceso de migración de MySQL a PostgreSQL con pgloader y barra de progreso
Terminal mostrando el proceso de migración de MySQL a PostgreSQL con pgloader y barra de progreso

MySQL ha servido bien a muchas empresas, pero llega un punto donde sus limitaciones empiezan a pesar — el manejo de JSON es más limitado que JSONB de PostgreSQL, las extensiones son escasas, la replicación tiene más restricciones y el cumplimiento del estándar SQL no es tan estricto. Si tu aplicación está creciendo y necesitas más potencia, flexibilidad y un camino claro de escalamiento, PostgreSQL es el siguiente paso natural.

En esta guía vas a migrar una base de datos MySQL completa a PostgreSQL usando pgloader — desde la planificación hasta la validación de que no se perdió ni un registro.

Antes de empezar: ¿realmente necesitas migrar?

No toda base de datos MySQL necesita migrar. Evalúa si te aplica alguno de estos escenarios:

  • Tu aplicación necesita JSONB para consultas complejas sobre datos semi-estructurados
  • Quieres extensiones como PostGIS (geoespacial), pg_trgm (búsqueda fuzzy) o pgvector (embeddings IA)
  • La replicación streaming de PostgreSQL se adapta mejor a tus necesidades de alta disponibilidad
  • Quieres eliminar costos de licencia (MySQL Enterprise) o restricciones del modelo Oracle
  • Tu equipo de desarrollo prefiere PostgreSQL y la aplicación no tiene dependencias duras con MySQL

Si tu aplicación funciona bien con MySQL y no necesitas nada de lo anterior, optimizar MySQL con nuestro servicio de bases de datos puede ser más práctico que migrar.

Plan de migración

Una migración exitosa tiene 5 fases. No saltes ninguna:

FaseDescripciónDuración típica
1. AnálisisInventario de tablas, tipos, funciones, triggers y queries específicas de MySQL2-3 días
2. Migración de esquema y datosConversión con pgloader + ajustes manuales1-2 días
3. Ajuste de aplicaciónReescribir queries incompatibles, ORM config, funciones3-10 días
4. PruebasRegresión completa, validación de datos, rendimiento3-5 días
5. CorteVentana de mantenimiento, migración final, switch1 día

Paso 1: Análisis de compatibilidad

Antes de tocar pgloader, analiza qué va a necesitar ajustes.

Diferencias de tipos de datos

MySQLPostgreSQLNotas
TINYINT(1)BOOLEANpgloader lo convierte automáticamente
INT AUTO_INCREMENTSERIAL o GENERATED ALWAYS AS IDENTITYConversión automática
DATETIMETIMESTAMPCompatible
DOUBLEDOUBLE PRECISIONCompatible
ENUM('a','b','c')TEXT + CHECK constraintRequiere atención
SET('x','y')TEXT[] (array)Requiere conversión manual
BLOB / LONGBLOBBYTEAConversión automática
JSONJSONBRecomendado cambiar a JSONB

Queries que necesitan ajuste

-- MySQL: comillas invertidas para identificadores
SELECT `order`.`id` FROM `order`;
-- PostgreSQL: comillas dobles (o sin comillas si no es palabra reservada)
SELECT "order"."id" FROM "order";

-- MySQL: LIMIT con OFFSET
SELECT * FROM productos LIMIT 10, 20;
-- PostgreSQL
SELECT * FROM productos LIMIT 20 OFFSET 10;

-- MySQL: GROUP_CONCAT
SELECT GROUP_CONCAT(nombre SEPARATOR ', ') FROM tags;
-- PostgreSQL
SELECT STRING_AGG(nombre, ', ') FROM tags;

-- MySQL: IF()
SELECT IF(stock > 0, 'Disponible', 'Agotado') FROM productos;
-- PostgreSQL
SELECT CASE WHEN stock > 0 THEN 'Disponible' ELSE 'Agotado' END FROM productos;

-- MySQL: IFNULL
SELECT IFNULL(telefono, 'Sin teléfono') FROM clientes;
-- PostgreSQL
SELECT COALESCE(telefono, 'Sin teléfono') FROM clientes;

-- MySQL: NOW() retorna DATETIME
-- PostgreSQL: NOW() retorna TIMESTAMP WITH TIME ZONE (más correcto)

Busca estos patrones en tu código antes de migrar. Si usas un ORM como SQLAlchemy, Prisma o TypeORM, la mayoría de estos ajustes se manejan cambiando el driver de conexión.

Paso 2: Instalar pgloader

pgloader es la herramienta estándar para migrar de MySQL a PostgreSQL. Lee directamente de MySQL, convierte tipos automáticamente y carga en PostgreSQL:

sudo apt install pgloader -y
pgloader --version

Paso 3: Preparar PostgreSQL destino

Crea la base de datos de destino vacía:

sudo -u postgres psql
CREATE USER app WITH PASSWORD 'contraseña_segura';
CREATE DATABASE miapp_pg OWNER app;
\q

Paso 4: Migrar con pgloader

Crea el archivo de comandos de pgloader:

cat > migracion.load <<'EOF'
LOAD DATABASE
  FROM mysql://root:mysql_password@localhost:3306/miapp_mysql
  INTO postgresql://app:contraseña_segura@localhost:5432/miapp_pg

WITH include drop,
     create tables,
     create indexes,
     reset sequences,
     downcase identifiers,
     batch rows = 1000,
     prefetch rows = 10000

SET maintenance_work_mem TO '512MB',
    work_mem TO '128MB'

-- Conversiones personalizadas
CAST type tinyint to boolean using tinyint-to-boolean,
     type int with extra auto_increment to serial,
     type bigint with extra auto_increment to bigserial

-- Excluir tablas que no necesitas migrar (sesiones, cache, etc.)
EXCLUDING TABLE NAMES MATCHING 'sessions', 'cache', 'migrations'

BEFORE LOAD DO
  $$ CREATE SCHEMA IF NOT EXISTS public; $$;

EOF

Ejecuta la migración:

pgloader migracion.load

pgloader muestra el progreso tabla por tabla con conteo de filas, errores y tiempo. Una migración típica de 10 GB toma 5-15 minutos.

Paso 5: Validar la migración

Conteo de registros

Compara el número de registros en cada tabla entre MySQL y PostgreSQL:

# Script de validación
cat > validar.py <<'PYTHON'
import mysql.connector
import psycopg2

# Conexiones
my = mysql.connector.connect(host='localhost', user='root', password='mysql_password', database='miapp_mysql')
pg = psycopg2.connect(host='localhost', user='app', password='contraseña_segura', dbname='miapp_pg')

my_cur = my.cursor()
pg_cur = pg.cursor()

# Obtener tablas de MySQL
my_cur.execute("SHOW TABLES")
tables = [t[0] for t in my_cur.fetchall()]

print(f"{'Tabla':<30} {'MySQL':>10} {'PostgreSQL':>12} {'OK':>5}")
print("-" * 60)

errors = 0
for table in tables:
    try:
        my_cur.execute(f"SELECT COUNT(*) FROM `{table}`")
        my_count = my_cur.fetchone()[0]

        pg_cur.execute(f"SELECT COUNT(*) FROM {table.lower()}")
        pg_count = pg_cur.fetchone()[0]

        ok = "✓" if my_count == pg_count else "✗"
        if my_count != pg_count:
            errors += 1

        print(f"{table:<30} {my_count:>10} {pg_count:>12} {ok:>5}")
    except Exception as e:
        print(f"{table:<30} {'ERROR':>10} {str(e)[:20]:>12}")
        errors += 1

print(f"\nResultado: {len(tables) - errors}/{len(tables)} tablas coinciden")

my.close()
pg.close()
PYTHON

python3 validar.py

Verificar secuencias

pgloader resetea las secuencias automáticamente, pero verifica:

-- En PostgreSQL: verificar que las secuencias están al valor correcto
SELECT schemaname, sequencename, last_value
FROM pg_sequences
WHERE schemaname = 'public';

Verificar constraints e índices

-- Constraints
SELECT table_name, constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'public'
ORDER BY table_name;

-- Índices
SELECT tablename, indexname, indexdef
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename;

Paso 6: Ajustar la aplicación

Cambiar el driver de conexión

Si tu aplicación usa un ORM, el cambio suele ser solo la cadena de conexión:

# SQLAlchemy — antes (MySQL)
DATABASE_URL = "mysql+pymysql://root:password@localhost/miapp"

# SQLAlchemy — después (PostgreSQL)
DATABASE_URL = "postgresql+psycopg2://app:password@localhost/miapp_pg"
// Prisma — schema.prisma
datasource db {
  // Antes
  // provider = "mysql"
  // Después
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

Ajustar queries raw

Si tienes queries SQL escritas directamente (no via ORM), revisa los patrones que listamos en el Paso 1 y reescríbelos. Los más comunes:

  • Comillas invertidas → comillas dobles o sin comillas
  • GROUP_CONCATSTRING_AGG
  • IFNULLCOALESCE
  • LIMIT offset, countLIMIT count OFFSET offset
  • NOW() devuelve timestamp con timezone en PostgreSQL

Paso 7: Pruebas de regresión

Antes de cortar a producción, ejecuta:

  1. Suite de tests — si tienes tests automatizados, ejecútalos contra PostgreSQL
  2. Pruebas manuales — recorre los flujos principales de la aplicación (crear registro, editar, buscar, reportes)
  3. Pruebas de rendimiento — compara tiempos de consultas críticas entre MySQL y PostgreSQL. PostgreSQL puede ser más rápido o más lento dependiendo de las queries — optimiza con EXPLAIN ANALYZE
  4. Prueba de backup — verifica que puedes respaldar y restaurar la base de datos PostgreSQL

Paso 8: Corte a producción

Cuando las pruebas pasan, programa la ventana de mantenimiento:

# 1. Poner la aplicación en mantenimiento
# 2. Exportar datos frescos de MySQL de producción
mysqldump --single-transaction -u root -p miapp > produccion_final.sql

# 3. Migrar con pgloader (usando los datos de producción)
pgloader migracion.load

# 4. Validar conteos
python3 validar.py

# 5. Cambiar la cadena de conexión en la aplicación
# 6. Reiniciar la aplicación apuntando a PostgreSQL
# 7. Verificar que todo funciona
# 8. Quitar modo mantenimiento

Mantén MySQL disponible

No apagues MySQL inmediatamente después del corte. Mantenlo en solo lectura durante 1-2 semanas como plan de rollback. Si algo falla en PostgreSQL que no detectaste en pruebas, puedes volver a MySQL rápidamente.

Siguientes pasos

Con la migración completada:

  • Optimizar PostgreSQL — ajustar shared_buffers, work_mem y crear índices específicos para tus queries más frecuentes
  • Replicación — configurar alta disponibilidad con streaming replication
  • Extensiones — aprovechar JSONB, pg_trgm para búsqueda fuzzy, PostGIS para datos geoespaciales
  • Monitoreo — dashboards de rendimiento de PostgreSQL con postgres_exporter
  • Bases de datos administradas — nuestro servicio DBA para que tu PostgreSQL siempre esté optimizado y protegido

Migración profesional

¿Necesitas migrar de MySQL a PostgreSQL sin riesgos?

Planificamos y ejecutamos la migración completa con validación de datos, ajuste de queries y acompañamiento post-migración.

Solicitar evaluación

Preguntas frecuentes

Temas relacionados

#postgresql#mysql#migracion#bases-de-datos#tutorial

¿Te fue útil? Compártelo

Artículos relacionados

Ver todos

Consultoría gratuita

¿Necesitas ayuda para migrar tu base de datos?

Planificamos y ejecutamos la migración de MySQL a PostgreSQL con validación completa, ajuste de queries y cero pérdida de datos.

Solicitar evaluación