Para optimizar consultas MySQL en una página web lenta, el primer paso es identificar exactamente qué consultas tardan más de lo esperado usando el slow query log o la instrucción EXPLAIN. Una vez localizadas, la solución casi siempre involucra añadir índices adecuados, reescribir joins ineficientes o activar la caché de consultas.
Este artículo te guía paso a paso desde el diagnóstico hasta la corrección, con ejemplos reales y herramientas gratuitas.
¿Cómo saber si MySQL es la causa de la lentitud?
Antes de modificar cualquier consulta, confirma que la base de datos es el cuello de botella. Señales claras:
- El TTFB (tiempo hasta el primer byte) supera los 800 ms pero el servidor tiene CPU y RAM disponibles.
- La lentitud aparece en páginas con listados, búsquedas o reportes, no en páginas estáticas.
- Al cargar la misma página varias veces seguidas, los tiempos son inconsistentes (tabla sin índice = escaneo completo en cada petición).
Herramientas de diagnóstico inicial:
- Query Monitor (plugin gratuito para WordPress): muestra cada consulta ejecutada, su duración y el stack de llamadas.
- New Relic o Datadog (de pago): trazas detalladas a nivel de transacción.
- MySQL slow query log: la opción nativa del motor, disponible sin instalar nada adicional.
Paso 1 — Activar y leer el Slow Query Log
El slow query log registra automáticamente toda consulta que supere un umbral de tiempo. Para activarlo temporalmente en sesión (sin reiniciar MySQL):
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- segundos; baja a 0.5 en producción si es necesario
SET GLOBAL slow_query_log_file = '/tmp/mysql-slow.log';
Deja correr el tráfico normal durante 15-30 minutos y luego analiza el log con mysqldumpslow:
mysqldumpslow -s t -t 10 /tmp/mysql-slow.log
El flag -s t ordena por tiempo total y -t 10 muestra las 10 peores. Copia las consultas candidatas para el siguiente paso.
Paso 2 — Analizar con EXPLAIN
Pon EXPLAIN delante de cualquier SELECT sospechoso:
EXPLAIN SELECT p.id, p.title, u.name
FROM posts p
JOIN users u ON u.id = p.user_id
WHERE p.status = 'published'
ORDER BY p.created_at DESC
LIMIT 20;
Las columnas más importantes del resultado:
| Columna | Valor problemático | Qué significa |
|---|---|---|
type |
ALL |
Escaneo completo de tabla; falta índice |
rows |
Número alto (miles) | MySQL revisa demasiadas filas para encontrar el resultado |
Extra |
Using filesort |
El ORDER BY no puede usar un índice; se ordena en disco |
Extra |
Using temporary |
MySQL crea una tabla temporal para resolver la consulta |
Si ves type: ALL en una tabla con más de 10,000 filas, añadir un índice es la corrección más impactante y rápida que puedes hacer.
Paso 3 — Crear índices donde se necesitan
Para la consulta del ejemplo anterior, los índices óptimos son:
-- Índice compuesto para el WHERE + ORDER BY
ALTER TABLE posts ADD INDEX idx_status_created (status, created_at);
-- Índice en la clave foránea del JOIN (si no existe)
ALTER TABLE posts ADD INDEX idx_user_id (user_id);
Reglas de oro para indexar:
- Indexa siempre las columnas que aparecen en
WHERE,JOIN ONyORDER BY. - Los índices compuestos son más eficientes que múltiples índices simples cuando las columnas siempre se filtran juntas.
- No indexes columnas con muy baja cardinalidad (ej. un booleano con solo dos valores posibles).
- Cada índice extra ralentiza las escrituras (
INSERT/UPDATE); no indexes todo, solo lo que EXPLAIN señala.
Paso 4 — Reescribir consultas ineficientes
A veces el índice existe pero la consulta está escrita de forma que MySQL no puede usarlo. Casos frecuentes:
Función en la columna indexada
-- MAL: la función YEAR() impide usar el índice en created_at
SELECT * FROM orders WHERE YEAR(created_at) = 2024;
-- BIEN: rango de fechas que sí usa el índice
SELECT * FROM orders
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
SELECT * innecesario
-- MAL: trae todas las columnas, incluyendo texto largo (e.g. post_content)
SELECT * FROM posts WHERE status = 'published';
-- BIEN: solo las columnas que la página necesita mostrar
SELECT id, title, excerpt, created_at FROM posts WHERE status = 'published';
Subconsulta en lugar de JOIN
-- MAL: subconsulta correlacionada, se ejecuta una vez por cada fila
SELECT id, (SELECT name FROM users WHERE id = posts.user_id) AS author
FROM posts;
-- BIEN: JOIN, mucho más eficiente con índice en users.id
SELECT posts.id, users.name AS author
FROM posts
JOIN users ON users.id = posts.user_id;
Para revisar más técnicas de optimización de bases de datos junto con estrategias de caché y compresión, visita el blog de rendimiento web.
Paso 5 — Caché de consultas y configuración del servidor
Si las consultas ya están bien escritas y tienen índices, evalúa las siguientes opciones de caché:
- Query Cache de MySQL (MySQL 5.7 o anterior): útil para lecturas muy repetitivas, pero obsoleto en MySQL 8 y MariaDB 10.5+.
- Caché a nivel de aplicación: guarda el resultado de consultas frecuentes en Redis o Memcached con un TTL corto (30-300 segundos). Es la solución más escalable.
- Ajustar
innodb_buffer_pool_size: en un servidor dedicado, este valor debe ser el 70-80 % de la RAM. En hosting compartido no lo controlas, pero vale la pena pedirle a tu proveedor que lo revise.
Si después de optimizar las consultas y la configuración el problema persiste, puede ser momento de migrar a un plan de hosting con más recursos o a una base de datos separada. El equipo de elenlace.com puede ayudarte a evaluar la arquitectura más adecuada para el volumen de tu sitio.
Conclusiones clave
- El slow query log es la herramienta gratuita más efectiva para identificar las consultas que más perjudican la velocidad.
EXPLAINrevela si falta un índice (type: ALL) o si el ordenado se hace en disco (Using filesort).- Añadir índices en columnas de
WHERE,JOINyORDER BYes la optimización de mayor impacto en la mayoría de los sitios. - Evita funciones sobre columnas indexadas,
SELECT *innecesarios y subconsultas correlacionadas. - La caché a nivel de aplicación (Redis/Memcached) es el paso lógico una vez que las consultas están optimizadas.
¿Tu sitio sigue lento después de aplicar estas técnicas? Los expertos de elenlace.com realizan auditorías de rendimiento completas, desde el análisis de consultas hasta la revisión del servidor, y te entregan un plan de acción concreto.
Preguntas frecuentes
¿Cuánto tiempo tarda una consulta MySQL para considerarse "lenta"?
El umbral estándar es 1 segundo, pero en páginas web la experiencia del usuario empieza a degradarse con consultas de más de 200-300 ms. En sitios de alto tráfico, cualquier consulta que supere los 100 ms en producción merece ser revisada.
¿Puedo optimizar MySQL en hosting compartido sin acceso SSH?
Sí. Puedes ejecutar EXPLAIN y ALTER TABLE ... ADD INDEX desde phpMyAdmin o Adminer sin necesitar acceso SSH. El slow query log requiere permisos de superusuario, pero plugins como Query Monitor (en WordPress) ofrecen información equivalente desde el panel de administración.
¿Añadir índices puede romper mi base de datos?
No. Un índice es una estructura de lectura; no modifica tus datos. Lo que sí puede ocurrir es que en tablas muy grandes (ALTER TABLE en millones de filas) la operación tarde varios minutos y bloquee escrituras. En ese caso usa pt-online-schema-change o gh-ost para añadir el índice sin bloqueo.
¿WordPress tiene herramientas específicas para diagnosticar consultas lentas?
Sí. El plugin gratuito Query Monitor muestra en el admin bar de WordPress todas las consultas ejecutadas en cada página, agrupadas por origen y ordenadas por duración. Es el punto de partida ideal para cualquier sitio WordPress antes de tocar la base de datos directamente.
Recursos útiles
Otros proveedores y guías que vale la pena comparar: