Cuando trabajamos con performance en MySQL, entender el plan de ejecución es fundamental.
EXPLAIN nos da una idea pero EXPLAIN ANALYZE nos dice la verdad.
Si hacemos:
EXPLAIN ANALYZE SELECT ...
Ejecuta la query (Ojo! si la query demora tanto que no termina la ejecución, ya esta no podemos usarlo) y devuelve:
- El plan de ejecución
- Métricas reales de ejecución
- Comparación entre estimaciones y realidad
Ejemplo típico:
-> Nested loop inner join (cost=0.787 rows=1.25)
(actual time=0.0644..0.0705 rows=1 loops=1)
-> Index lookup on tabla1 ...
(cost=...) (actual time=... rows=... loops=...)
-> Index lookup on tabla2 ...
(cost=...) (actual time=... rows=... loops=...)
Esto es un árbol de ejecución:
- De arriba hacia abajo
- Cada nodo es una operación
Conceptos clave (los más importantes):
cost
(cost=0.787 rows=1.25)
Es una estimación del optimizador.
Incluye:
- I/O esperado
- CPU estimado
- Lecturas de índice / tabla
⚠️ Importante:
❌ No es tiempo real
❌ No está en milisegundos
✔️ Solo sirve para comparar planes
rows (estimado)
rows=1.25
Cantidad estimada de filas.
Puede ser decimal porque:
- Es un promedio estadístico
- Ej: 1.25 filas por iteración
actual time
actual time=0.0644..0.0705
Tiempo real en milisegundos:
- Primer valor → tiempo hasta la primera fila
- Segundo valor → tiempo hasta la última fila
actual rows
rows=1
Filas reales procesadas.
loops
loops=1
Cuántas veces se ejecutó ese paso.
Relación clave
filas totales ≈ rows × loops
Tipos de operaciones (nodos del plan)
Nested Loop Join
MySQL usa principalmente este tipo de join.
Funcionamiento:
- Toma una fila de la tabla A
- Busca coincidencias en tabla B
- Repite
Index Lookup
Index lookup on tabla using index_name
Búsqueda usando índice:
✔️ Rápido
✔️ Ideal
Covering Index Lookup
El índice tiene todas las columnas necesarias.
✔️ No accede a la tabla
✔️ Más eficiente
Table Scan
Table scan on tabla
Escaneo completo.
❌ Costoso
❌ Señal de posible problema
Range Scan
Range scan on index
Usa índice, pero en un rango:
WHERE fecha BETWEEN ...
✔️ Bueno, pero no tan óptimo como lookup exacto
Filter
Filter: (condición)
Filtra filas después de leerlas.
⚠️ Si aparece mucho → puede faltar índice
Sort
Ordenamiento (`ORDER BY`)
❌ Puede usar memoria o disco
Temporary Table
Using temporary
MySQL crea tabla intermedia y puede impactar performance
Cómo leer un plan (estrategia real)
Paso 1: leer de abajo hacia arriba
- Primero las hojas (tablas base)
- Luego los joins
Paso 2: comparar estimado vs real
rows=1000 vs actual rows=10
Mala estimación → posible mal plan
Paso 3: mirar loops
Detecta:
- nested loops costosos
- operaciones repetidas
Paso 4: identificar scans
Buscar:
Table scan
Candidato a optimización
Problemas comunes detectables
1. Mala estimación
2. Full table scan innecesario
3. Nested loops costosos
4. Filtros tardíos
Veamos un ejemplo:
-> Nested loop inner join (rows=1.25)
(actual rows=1 loops=1)
-> Covering index lookup on autores
(rows=1 actual rows=1)
-> Index lookup on libros
(rows=1.25 actual rows=1)
Interpretación:
1. Encuentra 1 autor
2. Estima 1.25 libros por autor
3. Realmente hay 1
✔️ Plan correcto
✔️ Estimación aceptable
EXPLAIN ANALYZE es la herramienta más poderosa para entender performance en MySQL.
Pero requiere cambiar la mentalidad:
- No mirar solo el resultado
- Entender cómo MySQL llega a él













