Curso de MySQL
Optimización y rendimiento en MySQL: por dónde empezar
Por Víctor Peña · Publicado el

Hola, ¿cómo están? Continuando con el curso de MySQL, empezamos el último bloque: optimización.
Es la parte que separa una base de datos que funciona de una que sigue funcionando cuando la tabla tiene un millón de filas.
¡Empecemos!
Primero, mide
La regla número uno de la optimización:
No optimices lo que no has medido.
Es muy fácil pasar una tarde afinando una consulta que se ejecuta dos veces al día, mientras la que se ejecuta diez mil veces sigue lenta. Antes de tocar nada, averigua dónde está el problema.
Encontrar las consultas lentas
MySQL puede registrar automáticamente las consultas que tardan más de un umbral:
-- Ver la configuración actual
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
-- Activarlo
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- registrar las que pasen de 1 segundo
En producción esto se configura en el archivo my.cnf para que persista.
El registro te dice exactamente qué consultas revisar. Sin él, estás adivinando.
Para ver qué se está ejecutando en este momento:
SHOW PROCESSLIST;
Si ves una consulta llevando 40 segundos en estado Sending data, ya sabes por dónde empezar.
La causa número uno: falta de índices
En la inmensa mayoría de los casos, una consulta lenta es una consulta que recorre toda la tabla porque no encuentra un índice que usar.
Un índice funciona como el índice de un libro: en lugar de leer las 800 páginas buscando una palabra, vas directo a la página correcta.
-- Sin índice en 'estado': revisa el millón de filas
SELECT * FROM citas WHERE estado = 'programada';
-- Con índice: va directo a las que coinciden
CREATE INDEX idx_citas_estado ON citas(estado);
Es el tema de la siguiente lección, así que aquí solo el principio general: las columnas que aparecen en WHERE, JOIN y ORDER BY son candidatas a llevar índice.
Trae solo lo que necesitas
-- Innecesariamente pesado
SELECT * FROM citas JOIN pacientes ON ...;
-- Lo justo
SELECT c.fecha, c.costo, p.nombre FROM citas c JOIN pacientes p ON ...;
Cada columna extra son bytes que viajan del disco a la memoria y de la memoria a tu aplicación. Con una columna TEXT en la tabla, la diferencia es notable.
Y hay un beneficio menos evidente: si tu consulta pide solo columnas que están en un índice, MySQL puede responder sin tocar la tabla. Se llama índice cubriente y es de las optimizaciones más efectivas.
Limita el número de filas
SELECT * FROM citas ORDER BY fecha DESC LIMIT 50;
Nadie mira diez mil filas en pantalla. Si tu aplicación las trae todas para mostrar veinte, estás pagando por nada.
Y recuerda lo que vimos sobre OFFSET grande: paginar con LIMIT 20 OFFSET 500000 obliga a MySQL a recorrer medio millón de filas para descartarlas. En tablas grandes conviene paginar por identificador:
SELECT * FROM citas WHERE id > 500000 ORDER BY id LIMIT 20;
Filtra pronto y con precisión
Cuanto antes reduzcas el conjunto de filas, menos trabajo hay después:
-- Mejor: el WHERE reduce antes de agrupar
SELECT e.nombre, COUNT(*)
FROM citas c
JOIN doctores d ON c.doctor_id = d.id
JOIN especialidades e ON d.especialidad_id = e.id
WHERE c.fecha >= '2026-04-01'
GROUP BY e.id, e.nombre;
Y evita aplicar funciones sobre la columna que filtras:
-- Lento: la función impide usar el índice
WHERE YEAR(fecha) = 2026
-- Rápido: el índice sí sirve
WHERE fecha >= '2026-01-01' AND fecha < '2027-01-01'
Este detalle es de los que más rendimiento recuperan y menos se conocen. Si envuelves la columna en una función, el índice deja de servir.
Lo mismo con el comodín inicial en LIKE, como vimos:
WHERE apellido LIKE 'To%' -- usa índice
WHERE apellido LIKE '%rr%' -- no puede
El problema N+1
Este no es un problema de MySQL sino de cómo lo usa la aplicación, y es de los más frecuentes.
<?php
$citas = $pdo->query("SELECT * FROM citas")->fetchAll();
foreach ($citas as $cita) {
// Una consulta por cada cita
$sentencia = $pdo->prepare("SELECT nombre FROM pacientes WHERE id = ?");
$sentencia->execute([$cita['paciente_id']]);
}
Con 500 citas son 501 consultas. La solución es un JOIN:
SELECT c.fecha, c.costo, p.nombre
FROM citas c
JOIN pacientes p ON c.paciente_id = p.id;
Una consulta en lugar de 501. Lo desarrollamos en detalle en el problema N+1 en Laravel, pero el patrón es idéntico en cualquier lenguaje: si estás consultando dentro de un bucle, casi siempre falta un JOIN.
Elige bien los tipos de datos
Ya lo vimos al hablar de tipos de datos, y tiene impacto directo en el rendimiento:
- Una columna más pequeña significa más filas por página de memoria, y por tanto menos lecturas de disco.
- Un índice sobre
INTes más rápido que sobreVARCHAR(255). NOT NULLpermite a MySQL optimizar mejor que una columna que admite nulos.
En una tabla de diez millones de filas, cambiar un BIGINT innecesario por INT ahorra 40 MB solo en esa columna.
Normaliza, pero con criterio
La normalización evita duplicados y mantiene la coherencia. Es lo correcto por defecto.
Sin embargo, en sistemas con mucha lectura a veces se desnormaliza a propósito: guardar el total de una factura ya calculado en lugar de sumar sus líneas cada vez, o guardar el nombre del cliente junto al pedido para no hacer un JOIN en un listado que se consulta miles de veces.
Es una decisión consciente, con un costo: hay que mantener el dato duplicado sincronizado. No lo hagas por defecto; hazlo cuando midas que hace falta.
Optimiza también la escritura
- Inserta por lotes, no fila por fila. Como vimos en
INSERT, la diferencia es de órdenes de magnitud. - Usa transacciones para agrupar varias escrituras: MySQL confirma una vez en lugar de por cada sentencia.
- Cuidado con el exceso de índices. Cada índice acelera las lecturas y ralentiza las escrituras, porque hay que actualizarlo en cada
INSERT,UPDATEyDELETE.
Ese último punto es importante: los índices no son gratis. Poner uno en cada columna «por si acaso» empeora el rendimiento general.
Mantenimiento de tablas
-- Actualiza las estadísticas que usa el optimizador
ANALYZE TABLE citas;
-- Reorganiza y libera espacio tras muchos borrados
OPTIMIZE TABLE citas;
-- Comprueba si hay corrupción
CHECK TABLE citas;
OPTIMIZE TABLE bloquea la tabla mientras se ejecuta, así que en producción hazlo en horario de baja actividad.
Un método de trabajo
Cuando algo va lento, este es el orden que recomiendo:
- Identifica la consulta concreta, con el registro de consultas lentas.
- Analízala con
EXPLAIN, que veremos en dos lecciones. - Revisa los índices de las columnas del
WHEREy delJOIN. - Reduce lo que traes: columnas y filas.
- Revisa la aplicación, buscando consultas dentro de bucles.
- Mide otra vez para comprobar que mejoró.
El paso 6 es el que más se salta, y sin él no sabes si tu cambio sirvió de algo.
Errores comunes
- Optimizar sin medir.
- Poner índices en todas las columnas.
- Aplicar funciones sobre la columna filtrada.
SELECT *por costumbre.- Consultas dentro de bucles.
- Culpar al servidor cuando el problema es una consulta sin índice. Es lo habitual.
Para cerrar
La optimización en MySQL rara vez consiste en comprar un servidor más grande. Casi siempre es una consulta que recorre toda la tabla por falta de un índice, o una aplicación que hace 500 consultas donde bastaba una.
En la siguiente lección entramos de lleno en los índices, que es la herramienta que más impacto tiene.
Saludos y éxitos.
Norvic Software
Desarrollamos el software que tu empresa necesita
Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.
- Chatbots con Inteligencia Artificial
- Creación de agentes de IA
- Integración de IA en tus sistemas
- Desarrollo de software a medida
- Aplicaciones móviles iOS y Android
- Consultoría y asesoramiento técnico
Cotización sin costo · Respuesta directa por WhatsApp