Un índice es una estructura de datos auxiliar que MySQL mantiene junto a la tabla para acelerar las búsquedas. Sin índices, cada consulta con WHERE tiene que leer la tabla entera (full table scan); con el índice correcto, MySQL salta directamente a las filas que cumple la condición. La diferencia puede ser de milisegundos frente a minutos en tablas grandes.
En este capítulo cubrimos los tipos de índices, cómo crearlos, cómo EXPLAIN te dice si una consulta los usa, y cuándo un índice puede perjudicar en lugar de ayudar.
Cómo funciona un índice B-tree
El tipo de índice predeterminado en MySQL InnoDB es el B-tree (árbol balanceado). Imagina el índice de un libro: en vez de leer cada página buscando un tema, buscas el tema en el índice y vas directamente a la página correcta. El B-tree permite búsquedas exactas (=), rangos (BETWEEN, >, <) y prefijos de cadena (LIKE 'abc%') de forma eficiente. No ayuda con búsquedas de sufijo (LIKE '%abc').
La PRIMARY KEY ya es un índice
-- En InnoDB, la PRIMARY KEY es el índice clúster (clustered index): -- las filas se almacenan físicamente ordenadas por la PK. -- Por eso las búsquedas por PK son las más rápidas. SELECT * FROM clientes WHERE id = 42; -- usa el índice de la PK, muy rápido
Crear índices
-- Índice simple en una columna CREATE INDEX idx_ciudad ON clientes (ciudad); -- Índice único: no permite valores duplicados CREATE UNIQUE INDEX idx_email ON clientes (email); -- Equivalente a añadir UNIQUE en la definición de la columna -- Índice compuesto (multi-columna) CREATE INDEX idx_ciudad_fecha ON clientes (ciudad, fecha_alta); -- Índice en una tabla existente con ALTER TABLE ALTER TABLE pedidos ADD INDEX idx_estado_fecha (estado, fecha); -- Ver los índices de una tabla SHOW INDEXES FROM clientes; SHOW INDEXES FROM pedidos;
EXPLAIN: ver el plan de ejecución
-- Añadir EXPLAIN delante de cualquier SELECT para ver el plan EXPLAIN SELECT * FROM clientes WHERE ciudad = 'Madrid'; -- Columnas importantes del resultado de EXPLAIN: -- type: 'const' o 'ref' = usa índice (bueno); 'ALL' = full scan (malo) -- key: nombre del índice que usa (NULL si no usa ninguno) -- rows: estimación de filas a examinar (menor = mejor) -- Extra: 'Using index', 'Using where', 'Using filesort' (ordenación sin índice)
Ejemplo práctico: consulta sin índice vs. con índice:
-- Antes de crear el índice: EXPLAIN SELECT * FROM pedidos WHERE estado = 'pagado'; -- type: ALL, rows: 10000 — full table scan ALTER TABLE pedidos ADD INDEX idx_estado (estado); -- Después: EXPLAIN SELECT * FROM pedidos WHERE estado = 'pagado'; -- type: ref, key: idx_estado, rows: 200 — usa el índice
Índice compuesto: el orden de las columnas importa
-- Índice (ciudad, fecha_alta) sirve para: -- WHERE ciudad = 'Madrid' -- WHERE ciudad = 'Madrid' AND fecha_alta > '2024-01-01' -- ORDER BY ciudad, fecha_alta -- NO sirve (sin la primera columna) para: -- WHERE fecha_alta > '2024-01-01' ← necesita su propio índice -- Regla: el índice compuesto puede usarse por cualquier prefijo de columnas -- de izquierda a derecha, pero no por columnas no contiguas empezando por la derecha.
Cuándo los índices perjudican
- Inserciones y actualizaciones más lentas. Cada INSERT, UPDATE y DELETE tiene que actualizar también los índices. Una tabla con muchos índices se actualiza más lento.
- Tablas pequeñas. En tablas de pocas filas, el full scan es más rápido que navegar el B-tree. MySQL puede ignorar el índice en estos casos.
- Alta selectividad baja. Un índice en una columna con pocos valores distintos (como
estadocon 4 valores posibles) puede no reducir suficientemente las filas a examinar. - Funciones sobre la columna indexada.
WHERE YEAR(fecha) = 2026no usa el índice defecha. Mejor:WHERE fecha BETWEEN '2026-01-01' AND '2026-12-31'.
FULLTEXT e índices de texto
-- Índice FULLTEXT para búsquedas de texto completo
ALTER TABLE productos ADD FULLTEXT idx_ft_nombre (nombre);
-- Usar con MATCH...AGAINST (mucho más eficiente que LIKE '%texto%')
SELECT nombre, precio FROM productos
WHERE MATCH(nombre) AGAINST ('auricular' IN BOOLEAN MODE);
-- Para consultas de búsqueda avanzada, consulta el artículo
-- "Programar un buscador con PHP y MySQL" en este sitio.
Con esto termina el Curso de SQL completo. Los conceptos de todos los capítulos se complementan: los índices del capítulo 15 hacen más rápidos los JOINs del 9 y 10; las transacciones del 14 protegen los INSERT y UPDATE de los capítulos 12 y 13. Practica combinando técnicas de varios capítulos para escribir consultas eficientes y correctas.
