Optimizar consultas SQL lentas
¿Por qué optimizar consultas SQL?
Las consultas SQL lentas pueden afectar significativamente el rendimiento de tu aplicación, causando tiempos de carga largos, timeouts y una mala experiencia de usuario. Optimizar estas consultas mejora la velocidad, reduce la carga del servidor y mejora la escalabilidad.
Identificar consultas lentas
1. Habilitar log de consultas lentas
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
Agregá o modificá:
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow-query.log
long_query_time = 2
Reiniciá MySQL:
sudo systemctl restart mysql
2. Ver consultas lentas
sudo tail -f /var/log/mysql/slow-query.log
3. Usar EXPLAIN para analizar consultas
EXPLAIN SELECT * FROM usuarios WHERE email = '[email protected]';
Técnicas de optimización
1. Usar índices
Los índices son fundamentales para mejorar el rendimiento de las consultas.
Crear índices:
-- Índice simple
CREATE INDEX idx_email ON usuarios(email);
-- Índice único
CREATE UNIQUE INDEX idx_email_unique ON usuarios(email);
-- Índice compuesto
CREATE INDEX idx_nombre_edad ON usuarios(nombre, edad);
-- Índice en múltiples columnas
CREATE INDEX idx_busqueda ON productos(nombre, categoria, precio);
Ver índices existentes:
SHOW INDEXES FROM nombre_tabla;
Eliminar índices innecesarios:
DROP INDEX idx_nombre ON usuarios;
2. Optimizar consultas SELECT
Evitar SELECT *:
-- Malo
SELECT * FROM usuarios;
-- Bueno
SELECT id, nombre, email FROM usuarios;
Usar LIMIT:
-- Limitar resultados
SELECT * FROM productos ORDER BY fecha DESC LIMIT 10;
Usar WHERE apropiadamente:
-- Asegurate de que las columnas en WHERE tengan índices
SELECT * FROM usuarios WHERE email = '[email protected]';
3. Optimizar JOINs
Usar índices en columnas de JOIN:
-- Asegurate de que las columnas de JOIN tengan índices
SELECT u.nombre, p.titulo
FROM usuarios u
INNER JOIN posts p ON u.id = p.usuario_id
WHERE u.activo = 1;
Evitar JOINs innecesarios:
-- Si solo necesitás datos de una tabla, no hagas JOIN
-- Malo
SELECT u.nombre FROM usuarios u JOIN posts p ON u.id = p.usuario_id;
-- Bueno (si solo necesitás usuarios)
SELECT nombre FROM usuarios WHERE id IN (SELECT usuario_id FROM posts);
4. Optimizar subconsultas
Convertir subconsultas a JOINs cuando sea posible:
-- Menos eficiente
SELECT * FROM usuarios
WHERE id IN (SELECT usuario_id FROM posts WHERE activo = 1);
-- Más eficiente
SELECT DISTINCT u.*
FROM usuarios u
INNER JOIN posts p ON u.id = p.usuario_id
WHERE p.activo = 1;
5. Usar UNION en lugar de OR cuando sea posible
-- Menos eficiente
SELECT * FROM productos WHERE categoria = 'A' OR categoria = 'B';
-- Más eficiente
SELECT * FROM productos WHERE categoria = 'A'
UNION
SELECT * FROM productos WHERE categoria = 'B';
6. Optimizar ORDER BY
Usar índices para ORDER BY:
-- Crear índice para ordenamiento
CREATE INDEX idx_fecha ON posts(fecha);
-- La consulta será más rápida
SELECT * FROM posts ORDER BY fecha DESC;
Evitar ORDER BY con funciones:
-- Menos eficiente
SELECT * FROM usuarios ORDER BY UPPER(nombre);
-- Más eficiente (si es posible)
SELECT * FROM usuarios ORDER BY nombre;
7. Optimizar GROUP BY
Usar índices en columnas de GROUP BY:
CREATE INDEX idx_categoria ON productos(categoria);
-- La consulta será más rápida
SELECT categoria, COUNT(*) FROM productos GROUP BY categoria;
8. Usar EXISTS en lugar de COUNT cuando sea posible
-- Menos eficiente
SELECT * FROM usuarios
WHERE (SELECT COUNT(*) FROM posts WHERE usuario_id = usuarios.id) > 0;
-- Más eficiente
SELECT * FROM usuarios u
WHERE EXISTS (SELECT 1 FROM posts p WHERE p.usuario_id = u.id);
9. Evitar funciones en WHERE
-- Menos eficiente (no puede usar índice)
SELECT * FROM usuarios WHERE YEAR(fecha_registro) = 2024;
-- Más eficiente
SELECT * FROM usuarios
WHERE fecha_registro >= '2024-01-01' AND fecha_registro < '2025-01-01';
10. Usar LIMIT con OFFSET eficientemente
-- Para paginación, considera usar cursor-based pagination
-- En lugar de:
SELECT * FROM productos ORDER BY id LIMIT 10 OFFSET 1000;
-- Usá:
SELECT * FROM productos WHERE id > 1000 ORDER BY id LIMIT 10;
Herramientas de análisis
1. EXPLAIN
EXPLAIN SELECT * FROM usuarios WHERE email = '[email protected]';
Revisá especialmente: - type: Debe ser ref o eq_ref, no ALL - key: Debe mostrar el índice usado - rows: Número de filas examinadas (debe ser bajo)
2. EXPLAIN ANALYZE (MySQL 8.0.18+)
EXPLAIN ANALYZE SELECT * FROM usuarios WHERE email = '[email protected]';
3. PROFILE
SET PROFILING = 1;
SELECT * FROM usuarios WHERE email = '[email protected]';
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;
Optimizar tablas
1. Analizar tablas regularmente
ANALYZE TABLE nombre_tabla;
2. Optimizar tablas
OPTIMIZE TABLE nombre_tabla;
3. Verificar y reparar tablas
CHECK TABLE nombre_tabla;
REPAIR TABLE nombre_tabla;
Configuración de MySQL para mejor rendimiento
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
Parámetros importantes:
[mysqld]
# Memoria
innodb_buffer_pool_size = 1G
key_buffer_size = 256M
query_cache_size = 64M
# Consultas
max_connections = 200
thread_cache_size = 50
table_open_cache = 2000
# Logs
slow_query_log = 1
long_query_time = 2
Reiniciá MySQL después de los cambios.
Mejores prácticas
- Usá índices estratégicamente: No crees índices en todas las columnas, solo en las que se usan frecuentemente en WHERE, JOIN y ORDER BY
- Monitoreá consultas lentas: Revisá regularmente el log de consultas lentas
- Probá cambios: Siempre probá las optimizaciones en un entorno de desarrollo primero
- Documentá cambios: Guardá un registro de las optimizaciones realizadas
- Hacé backups: Antes de hacer cambios importantes, hacé un backup
- Actualizá estadísticas: Ejecutá ANALYZE TABLE regularmente
¿Necesitás ayuda?
Si tenés consultas SQL lentas y necesitás ayuda optimizándolas, abrí un ticket desde el panel de billing.baires.host o contactanos por soporte. Incluí la consulta problemática y el resultado de EXPLAIN.
También podés escribirnos por nuestras redes:
- Instagram: @baires_host
- Discord (soporte activo): https://discord.gg/dzjauatAFN
- Linktree: https://linktr.ee/baires.host