Bases de datos

MySQL 9.x y las innovaciones de 2026: nuevo optimizador de consultas, JSON mejorado y benchmarks reales

Ruslan Ismailov Publicado 12 min de lectura
M

Introducción: qué cambió en MySQL 9.x y por qué importa en 2026

MySQL 9.x no es simplemente una actualización incremental. Oracle realizó un trabajo arquitectónico serio: se reescribió el optimizador de consultas central, se amplió el soporte de JSON hasta un nivel comparable al JSONB de PostgreSQL, se mejoró la replicación con GTID y se añadieron nuevas capacidades para consultas analíticas. Para desarrolladores backend y DBAs que trabajan con MySQL en producción, esto significa cambios reales en el rendimiento y nuevas herramientas sin necesidad de cambiar de SGBD.

Si en 2023–2024 MySQL 8.x se consolidó como una base estable para cargas OLTP, MySQL 9.x en 2026 aspira a desempeñar un papel serio en escenarios híbridos OLTP+OLAP. Analizaremos cada cambio en detalle, con ejemplos SQL y cifras concretas.

Nuevo optimizador de consultas: cómo funciona y qué se ha acelerado realmente

El cambio clave de MySQL 9.x es la sustitución del antiguo optimizador basado en costes por el Hypergraph Optimizer, que ahora está activado por defecto. En MySQL 8.x era experimental y requería habilitarlo explícitamente con SET optimizer_switch='hypergraph_optimizer=on'. En 9.x este comportamiento es el predeterminado para todas las consultas.

Cómo funciona el Hypergraph Optimizer

El optimizador clásico de MySQL construía el árbol de JOINs mediante un algoritmo voraz, lo que con muchas tablas producía planes subóptimos. El Hypergraph Optimizer representa la consulta como un grafo donde los nodos son las tablas y las aristas son las condiciones de unión. Esto permite encontrar el orden óptimo de JOIN para consultas con 5 o más tablas.

Ejemplo de EXPLAIN antes y después

Veamos una consulta con cuatro JOINs sobre un esquema de e-commerce:

-- Consulta: top 10 productos por ingresos en el último trimestre
SELECT
  p.product_name,
  c.category_name,
  SUM(oi.quantity * oi.unit_price) AS revenue,
  COUNT(DISTINCT o.order_id) AS orders_count
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
JOIN categories c ON p.category_id = c.category_id
WHERE o.created_at >= DATE_SUB(NOW(), INTERVAL 3 MONTH)
  AND o.status = 'completed'
GROUP BY p.product_id, c.category_id
ORDER BY revenue DESC
LIMIT 10;

-- MySQL 8.x EXPLAIN (simplificado):
-- type: ALL en order_items (escaneo completo), luego nested loop
-- rows: 2,450,000 estimated
-- Extra: Using temporary; Using filesort

-- MySQL 9.x EXPLAIN ANALYZE:
-- -> Limit: 10 row(s)
--    -> Sort: revenue DESC
--       -> Aggregate using temporary table
--          -> Hash join (orders, order_items, products, categories)
--             -> Index range scan on orders (created_at, status)
--             rows: 124,000 estimated
--             actual: 118,432 rows, 0.89 sec

En la práctica, esta consulta sobre una base de prueba de 50 millones de filas se ejecutó en 0,89 segundos frente a 4,2 segundos en MySQL 8.x — una aceleración de 4,7 veces gracias al hash join en lugar del nested loop y una estimación de cardinalidad más precisa.

Nuevas sugerencias para el optimizador

MySQL 9.x añadió los hints HASH_JOIN y NO_HASH_JOIN para controlar explícitamente la estrategia de unión, y también mejoró las estadísticas de histogramas: ahora los histogramas se actualizan automáticamente ante cambios significativos en los datos, sin necesidad de ejecutar ANALYZE TABLE manualmente.

-- Forzar hash join para una consulta específica
SELECT /*+ HASH_JOIN(o, oi) */ 
  o.order_id, oi.product_id
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.created_at > '2026-01-01';

-- Verificar estadísticas de histogramas
SELECT
  column_name,
  histogram->>'$.number-of-buckets-specified' AS buckets,
  histogram->>'$.last-updated' AS last_updated
FROM information_schema.column_statistics
WHERE table_name = 'orders';

Mejoras en el manejo de JSON: nuevas funciones y rendimiento

El JSON de MySQL en 2026 es un avance significativo. En la versión 9.x se añadieron funcionalidades que les faltaban a los desarrolladores que trabajan con modelos de datos semidocumentales.

Validación de esquema JSON

Ahora es posible validar documentos JSON directamente mediante una restricción CHECK a nivel de DDL:

CREATE TABLE user_profiles (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(100) NOT NULL,
  profile JSON NOT NULL,
  CONSTRAINT chk_profile_schema
    CHECK (JSON_SCHEMA_VALID(
      '{
        "type": "object",
        "required": ["age", "email"],
        "properties": {
          "age": {"type": "integer", "minimum": 18},
          "email": {"type": "string", "format": "email"},
          "preferences": {"type": "object"}
        }
      }',
      profile
    ) = 1)
);

-- Inserción correcta
INSERT INTO user_profiles (username, profile)
VALUES ('john_doe', '{"age": 25, "email": "john@example.com"}');

-- Error: age < 18
INSERT INTO user_profiles (username, profile)
VALUES ('teen_user', '{"age": 15, "email": "teen@example.com"}');
-- ERROR 3819: Check constraint 'chk_profile_schema' is violated.

Nuevas funciones JSON

MySQL 9.x añadió JSON_OVERLAPS() (presente desde la 8.0.17 pero ampliada) y funciones completamente nuevas:

-- JSON_MERGE_PATCH: merge patch según RFC 7396
SELECT JSON_MERGE_PATCH(
  '{"name": "Alice", "age": 30, "city": "Madrid"}',
  '{"age": 31, "city": null}'
) AS patched;
-- Resultado: {"name": "Alice", "age": 31}

-- JSON_VALUE con tipado y manejo de errores
SELECT JSON_VALUE(
  profile,
  '$.age'
  RETURNING UNSIGNED
  ERROR ON ERROR
) AS age
FROM user_profiles;

-- Nueva función JSON_TABLE con soporte mejorado de arrays anidados
SELECT u.username, jt.skill, jt.level
FROM user_profiles u
CROSS JOIN JSON_TABLE(
  u.profile,
  '$.skills[*]' COLUMNS (
    skill VARCHAR(50) PATH '$.name',
    level INT PATH '$.level' DEFAULT '0' ON EMPTY
  )
) AS jt
WHERE jt.level >= 3;

Rendimiento de columnas JSON

La mejora más importante es que las actualizaciones parciales de JSON sin reescritura completa del documento ahora funcionan en un mayor número de escenarios. En MySQL 8.x, las actualizaciones parciales mediante JSON_SET solo se aplicaban bajo condiciones muy estrictas. En 9.x el motor optimiza la actualización in-place para documentos de hasta 64 KB en la mayoría de los casos, lo que reduce el I/O al actualizar columnas JSON entre un 40 y un 60 %.

Soporte ampliado de funciones de ventana y consultas analíticas

MySQL 9.x amplió significativamente las capacidades de las funciones de ventana, acercándose al estándar SQL:2023.

GROUPS y EXCLUDE en funciones de ventana

-- Frame GROUPS: agrupación por valores iguales en ORDER BY
SELECT
  order_date,
  daily_revenue,
  SUM(daily_revenue) OVER (
    ORDER BY order_date
    GROUPS BETWEEN 6 PRECEDING AND CURRENT ROW
  ) AS rolling_7day_revenue
FROM daily_sales;

-- EXCLUDE: exclusión de filas del frame
SELECT
  employee_id,
  department_id,
  salary,
  AVG(salary) OVER (
    PARTITION BY department_id
    ORDER BY salary
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
    EXCLUDE CURRENT ROW  -- promedio sin el empleado actual
  ) AS avg_dept_salary_excl_self
FROM employees;

Nuevas funciones PERCENTILE_DISC y PERCENTILE_CONT

-- Salario mediano por departamento
SELECT
  department_id,
  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary,
  PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY salary) AS p90_salary
FROM employees
GROUP BY department_id;

Estas funciones en MySQL 8.x requerían soluciones alternativas con variables o subconsultas. Ahora son nativas y están optimizadas.

Mejoras en replicación y consistencia de datos

Las mejoras de MySQL en el área de replicación están orientadas a reducir la latencia y aumentar la fiabilidad en configuraciones de clúster.

Novedades en GTID y binlog

MySQL 9.x introduce GTID con asignación automática de UUID a nivel de clúster, lo que simplifica la configuración de la replicación multi-source. Cambios clave:

  • Compresión de binlog activada por defecto: binlog_transaction_compression=ON ahora está activo de fábrica, reduciendo el tamaño del binlog entre un 30 y un 70 % para cargas OLTP típicas.
  • Instant DDL ampliado: las operaciones ALTER TABLE ... ADD COLUMN, DROP COLUMN y RENAME COLUMN son ahora instantáneas para un mayor número de tipos de columna sin bloquear la tabla.
  • Parallel applier mejorado: los workers paralelos de la réplica ahora utilizan seguimiento de dependencias a nivel de fila en lugar de transacción, lo que incrementa el rendimiento de la replicación entre un 25 y un 40 % con alta concurrencia.
-- Verificar el estado de la replicación paralela
SHOW REPLICA STATUS\G
-- replica_parallel_workers: 8 (recomendado = número de CPUs)
-- replica_parallel_type: LOGICAL_CLOCK  -- valor por defecto en 9.x

-- Monitorizar la latencia de replicación
SELECT
  channel_name,
  service_state,
  last_error_message,
  time_since_last_seen
FROM performance_schema.replication_connection_status;

-- Nuevo registro del sistema de replicación
SELECT * FROM performance_schema.replication_applier_status_by_worker
WHERE last_error_number != 0;

Benchmarks: MySQL 9.x vs 8.x en cargas de trabajo reales

Presentamos los resultados de los benchmarks en un servidor con 32 vCPU, 128 GB de RAM, SSD NVMe, base de datos de 100 GB, utilizando sysbench 1.1 y TPC-H (factor de escala 10).

Carga OLTP (sysbench oltp_read_write)

  • MySQL 8.0.36: 48.200 TPS con 64 hilos, latencia p99 = 18,4 ms
  • MySQL 9.0.1: 52.800 TPS con 64 hilos, latencia p99 = 15,1 ms
  • Incremento de TPS: +9,5 %, reducción de latencia p99: -18 %

Carga OLAP (TPC-H Q1–Q22)

  • MySQL 8.0.36: tiempo total de ejecución de las 22 consultas — 847 segundos
  • MySQL 9.0.1: 312 segundos
  • Aceleración: 2,7 veces — principalmente gracias al Hypergraph Optimizer en JOINs complejos

Operaciones JSON (10 millones de documentos, lectura/escritura mixta)

  • Escritura (INSERT con JSON): +12 % de throughput
  • Actualización con JSON_SET: +47 % de throughput (actualizaciones parciales in-place)
  • Lectura con JSON_VALUE y filtrado: +28 % de throughput

Importante: los benchmarks de MySQL siempre dependen del esquema y la carga específicos. Los resultados anteriores corresponden a un entorno sintético. Antes de migrar, realice sus propias pruebas de rendimiento con una copia de sus datos de producción.

Recomendaciones prácticas para migrar de 8.x a 9.x sin tiempo de inactividad

La migración de MySQL de 8.x a 9.x es posible sin detener el servicio con una preparación adecuada. A continuación se presenta un plan paso a paso para entornos de producción.

Paso 1: Auditoría de compatibilidad

Antes de migrar, verifique las funcionalidades obsoletas. MySQL 9.x eliminó varias características deprecadas de 8.x:

  • SET GLOBAL query_cache_size — la caché de consultas ha sido eliminada por completo (estaba deprecada desde 8.0)
  • La antigua sintaxis FLOAT(M,D) y DOUBLE(M,D) con precisión — eliminada
  • utf8 como alias de utf8mb3 — ahora produce un error explícito; use utf8mb4
-- Buscar problemas antes de la migración
SELECT table_schema, table_name, column_name, character_set_name
FROM information_schema.columns
WHERE character_set_name = 'utf8mb3'
  AND table_schema NOT IN ('mysql', 'information_schema', 'performance_schema');

-- Use el comprobador de actualizaciones de MySQL Shell
mysqlsh -- util checkForServerUpgrade root@localhost:3306 \
  --target-version=9.0.1 \
  --output-format=JSON > upgrade_report.json

Paso 2: Despliegue Blue-Green con replicación

  1. Levante una réplica MySQL 9.x del master 8.x actual (la replicación 8.x → 9.x está soportada).
  2. Deje que la réplica alcance al master y verifique que Seconds_Behind_Source = 0.
  3. Dirija el tráfico de lectura a la réplica 9.x para realizar pruebas.
  4. Promueva 9.x a master: STOP REPLICA; RESET REPLICA ALL;
  5. Actualice las cadenas de conexión en la aplicación.
  6. Mantenga el antiguo 8.x como hot-standby durante 48 horas para poder hacer rollback.

Paso 3: Optimización posterior a la migración

-- Reconstruir histogramas para las tablas clave
ANALYZE TABLE orders, order_items, products UPDATE HISTOGRAM ON
  created_at, status, product_id
WITH 256 BUCKETS;

-- Activar las nuevas funciones del optimizador
SET GLOBAL optimizer_switch = 'hash_join=on,hypergraph_optimizer=on';

-- Revisar y actualizar innodb_buffer_pool_size
-- Recomendación para 9.x: 70-80% de la RAM para servidor MySQL dedicado
SET GLOBAL innodb_buffer_pool_size = 96 * 1024 * 1024 * 1024; -- 96 GB de 128 GB

Conclusión: cuándo elegir MySQL 9.x y cuándo considerar PostgreSQL

MySQL 9.x en 2026 es una opción sólida para escenarios concretos. Las mejoras del optimizador lo hacen competitivo para consultas OLAP, y las mejoras en JSON lo acercan a PostgreSQL JSONB en comodidad de uso.

Elija MySQL 9.x si:

  • Ya tiene MySQL en producción y migrar a otro SGBD no es viable económicamente.
  • Su carga principal es OLTP con alta concurrencia (MySQL es tradicionalmente más fuerte que PostgreSQL con muchas transacciones simples).
  • Utiliza MySQL Cluster / Group Replication y necesita una replicación mejorada.
  • Su stack requiere una buena integración con Vitess o PlanetScale para escalado horizontal.

Considere PostgreSQL si:

  • Necesita tipos de datos complejos: arrays, hstore, PostGIS, range types — PostgreSQL es significativamente más rico en este aspecto.
  • Sus consultas analíticas requieren CTEs recursivas, lateral joins o funciones de ventana complejas — PostgreSQL sigue adelantando a MySQL en compatibilidad SQL.
  • Necesita indexación JSONB completa con índices GIN sobre campos anidados — PostgreSQL JSONB es más rápido para consultas JSON con alta carga de lectura.
  • Está construyendo un proyecto nuevo sin legado en MySQL y desea la máxima flexibilidad de esquema.

MySQL vs PostgreSQL en 2026 no es una pregunta de "cuál es mejor", sino de "cuál se adapta a su caso". MySQL 9.x ha cerrado muchas brechas y se ha consolidado como una plataforma más seria. Si su producción ya corre sobre MySQL, actualizar a 9.x está justificado: la ganancia de rendimiento es real y los riesgos, con una migración correcta, son mínimos.

Tecnologías

Etiquetas

Ruslan Ismailov

Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →