Los índices son la herramienta más importante para optimizar el rendimiento de MySQL. Sin el índice correcto, una consulta sobre una tabla con un millón de filas puede tardar segundos; con el índice adecuado, se resuelve en microsegundos. Pero los índices también tienen un coste: ocupan espacio y ralentizan las escrituras, porque cada INSERT, UPDATE y DELETE debe actualizar también los índices.
En este capítulo cubrimos los tipos de índices que soporta MySQL InnoDB, cómo crearlos, cómo verificar con EXPLAIN que se usan, y las pautas para decidir qué columnas indexar.
Tipos de índices en MySQL InnoDB
|
Tipo | Descripción |
PRIMARY KEY | Índice clúster: las filas se almacenan ordenadas por la PK. Solo puede haber uno por tabla. |
UNIQUE | Como INDEX pero no permite duplicados. Null sí está permitido (varias filas pueden ser NULL). |
INDEX (KEY) | Índice secundario B-tree estándar. Acelera búsquedas, rangos y ordenaciones. |
FULLTEXT | Para búsquedas de texto completo con MATCH...AGAINST. Soportado en InnoDB desde MySQL 5.6. |
SPATIAL | Para tipos de datos geométricos (POINT, POLYGON, etc.). |
Crear índices
-- Al crear la tabla
CREATE TABLE pedidos (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
id_cliente INT UNSIGNED NOT NULL,
fecha DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
estado VARCHAR(20) NOT NULL,
PRIMARY KEY (id),
INDEX idx_cliente (id_cliente),
INDEX idx_estado_fecha (estado, fecha) -- índice compuesto
);
-- En una tabla existente
CREATE INDEX idx_ciudad ON clientes (ciudad);
CREATE UNIQUE INDEX idx_email ON clientes (email);
-- Con ALTER TABLE
ALTER TABLE pedidos ADD INDEX idx_fecha (fecha);
ALTER TABLE pedidos ADD UNIQUE KEY uk_referencia (referencia_externa);
-- Ver los índices de una tabla
SHOW INDEXES FROM pedidos;
Eliminar índices
DROP INDEX idx_ciudad ON clientes; ALTER TABLE clientes DROP INDEX idx_ciudad; -- equivalente
EXPLAIN: verificar el uso de índices
-- Ver el plan de ejecución EXPLAIN SELECT * FROM pedidos WHERE estado = 'pagado' AND fecha > '2026-01-01'\G -- Columnas clave del resultado: -- type: 'const' = PK lookup (óptimo); 'ref' o 'range' = usa índice; 'ALL' = full scan (lento) -- key: nombre del índice usado (NULL = ninguno) -- rows: estimación de filas a examinar -- Extra: 'Using index' = cubierto por índice; 'Using filesort' = ordenación sin índice -- EXPLAIN ANALYZE (MySQL 8.0.18+): ejecuta la consulta y muestra tiempos reales EXPLAIN ANALYZE SELECT * FROM pedidos WHERE estado = 'pagado'\G
Índices compuestos: el orden importa
-- Índice (estado, fecha) sirve para: -- WHERE estado = 'pagado' ← usa el índice (prefijo) -- WHERE estado = 'pagado' AND fecha > '2026-01-01' ← usa el índice completo -- ORDER BY estado, fecha ← usa el índice para ordenar -- NO sirve para: -- WHERE fecha > '2026-01-01' ← no empieza por la primera columna del índice -- Añadir un índice separado para fecha si también necesitas buscar solo por fecha: CREATE INDEX idx_fecha ON pedidos (fecha);
Cuándo añadir un índice
Candidatos buenos para un índice:
- Columnas que aparecen frecuentemente en
WHERE,JOIN ONuORDER BY - Claves foráneas (InnoDB no las indexa automáticamente — debes hacerlo manualmente)
- Columnas con alta cardinalidad (muchos valores distintos: emails, IDs)
Candidatos malos:
- Columnas con baja cardinalidad (estado con 3 valores posibles puede no valer la pena)
- Columnas raramente usadas en WHERE
- Tablas muy pequeñas (el full scan es más rápido que navegar el B-tree)
- Tablas con escrituras muy frecuentes (cada índice extra ralentiza los INSERTs)
