Migrar de MySQL 5.6 o 5.7 a 8.0 no es un trámite cosmético ni “cambiar un número de versión”. El salto toca compatibilidad, comportamiento del optimizador, collations, autenticación y, sobre todo, expectativas de aplicaciones que llevan años asumiendo un comportamiento que en 8.0 cambia. El error fundamental es asumir que hay compatibilidad solo porque el esquema importa sin quejarse.

Nota importante antes de empezar: MySQL no soporta el salto directo de 5.6 a 8.0 con upgrade in-place. El camino obligado es 5.6 → 5.7 → 8.0. Si alguien te dice que se salta ese peldaño, desconfía.

Paso previo obligatorio: el inventario honesto

Antes de tocar nada, necesitas saber qué tienes realmente. No lo que crees que tienes: lo que hay. Engines por tabla, collations mixtas, procedimientos y triggers olvidados en algún sótano de la base.

-- Listar engines por tabla
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys');

-- Revisar collations mixtas
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_COLLATION NOT LIKE 'utf8mb4%'
  AND TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema','sys');

-- Examinar routines y triggers
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA NOT IN ('sys');

SELECT TRIGGER_SCHEMA, TRIGGER_NAME FROM information_schema.TRIGGERS;

Lo que suele romper primero

El plugin de autenticación cambió (y tus drivers no se enteraron)

MySQL 8.0 usa caching_sha2_password por defecto, reemplazando a mysql_native_password. Drivers antiguos (PHP < 7.4, conectores JDBC viejos) no soportan el nuevo método y fallan con errores crípticos que no mencionan la palabra “autenticación” por ningún lado. Es de los primeros golpes que te llevas.

-- Verificar plugin de cada usuario existente
SELECT user, plugin FROM mysql.user
WHERE user NOT IN ('mysql.sys','mysql.session','mysql.infoschema');

-- Crear usuario compatible con drivers antiguos
CREATE USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

-- O cambiar el plugin de un usuario existente:
ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

Collations: el problema silencioso

MySQL 8.0 cambia el collation por defecto de utf8mb4_unicode_ci a utf8mb4_0900_ai_ci. Suena inofensivo hasta que un JOIN entre dos tablas con collations distintos revienta con un illegal mix of collations, o hasta que restauras un dump y las comparaciones dejan de comportarse como esperabas. La estrategia segura es aburrida pero funciona: normaliza todo a utf8mb4_unicode_ci de forma explícita antes de migrar, no después.

Tipos de datos y diseño heredado

El límite de ancho de fila en InnoDB es 65.535 bytes. Columnas VARCHAR con caracteres multibyte (utf8mb4 usa hasta 4 bytes por carácter) pueden superar ese límite al migrar si las tablas eran latin1 en origen. Lo simpático es que esto no aparece en pruebas pequeñas: aparece con la tabla ancha que nadie quería tocar.

-- Detectar tablas con potencial desbordamiento de fila
SELECT TABLE_NAME, SUM(CHARACTER_MAXIMUM_LENGTH * 4) AS estimated_row_bytes
FROM information_schema.COLUMNS
WHERE DATA_TYPE IN ('varchar','char')
  AND TABLE_SCHEMA = 'tu_base'
GROUP BY TABLE_NAME
HAVING estimated_row_bytes > 65535;

Engines heredados: MyISAM y el olvido

MyISAM pierde soporte de transacciones y crash recovery confiable en 8.0. Todas las tablas MyISAM deberían convertirse a InnoDB antes de migrar. Una tabla MyISAM en 8.0 puede corromperse sin recovery transaccional posible, y ahí ya no hay conversación técnica: hay pérdida de datos.

-- Convertir una tabla a InnoDB
ALTER TABLE nombre_tabla ENGINE=InnoDB;

-- Generar los ALTER en masa para todas las MyISAM
SELECT CONCAT('ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '` ENGINE=InnoDB;')
FROM information_schema.TABLES
WHERE ENGINE = 'MyISAM'
  AND TABLE_SCHEMA NOT IN ('mysql','information_schema');

Routines, triggers y los sótanos técnicos

Los procedimientos almacenados conservan el sql_mode con el que fueron creados. Si nacieron en 5.6 con un sql_mode permisivo, al recrearlos en 8.0 —con un modo más estricto por defecto— pueden fallar o, peor, comportarse distinto sin avisar. Estos son los sótanos: nadie los mira hasta que hacen ruido en producción.

SQL mode: el comportamiento que cambia sin avisar

MySQL 8.0 activa por defecto modos más estrictos: STRICT_TRANS_TABLES, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO. Esto rompe patrones comunes de aplicaciones antiguas: insertar '0000-00-00' en un campo DATE, o dividir por cero en una query calculada, pasan de ser toleradas a lanzar error. Cosas que “siempre funcionaron” dejan de funcionar el día de la migración.

Checklist pre-migración

  1. Inventario de objetos: tablas, vistas, procedimientos, triggers, funciones.
  2. Validación con mysqlcheck --all-databases --check --auto-repair.
  3. Dump completo con --routines --triggers --events antes de cualquier cambio.
  4. Probar la restauración del dump en una instancia limpia de 8.0.
  5. Ejecutar el Upgrade Checker (disponible desde MySQL 5.7.14).
  6. Tener un rollback probado, no imaginado.

¿Prefieres que lo haga alguien que ya pisó estas minas?

Nada de esto es imposible; simplemente tiene muchas aristas donde un descuido se paga en producción. En Reactiv hacemos exactamente este tipo de migraciones MySQL —con inventario honesto, pruebas funcionales reales y rollback probado— como parte de nuestros servicios de administración de servidores y bases de datos. Si tienes una migración por delante y prefieres no descubrir las sorpresas en vivo, conversemos.

Conclusión

Una migración bien planificada entre versiones mayores de MySQL tiene un camino conocido. El problema nunca es que sea imposible: es que requiere inventario honesto, pruebas funcionales reales y un rollback probado antes de tocar producción. La validación no termina cuando el servicio arranca; termina cuando comparaste planes de ejecución, corriste tus pruebas y confirmaste que lo que antes funcionaba, sigue funcionando.