Índices de base de datos
Por qué una consulta pasa de segundos a milisegundos con un índice, cómo funcionan por dentro y cuándo un índice no te salva.
En esta página
Sin índice, buscar una fila es leer la tabla entera — un full scan: con 10 millones de usuarios, tu WHERE email = ... recorre 10 millones de filas para devolver una. El índice es la diferencia entre segundos y milisegundos, y entenderlo es la mejora de rendimiento con mejor relación esfuerzo/resultado que existe en bases de datos.
La idea: el índice de un libro
Un índice es una estructura ordenada aparte que mapea valores de una columna → dónde está su fila. Igual que el índice alfabético de un libro: no relees el libro para encontrar “recursión”; saltas a la R, y de ahí a la página.
Y en una estructura ordenada se busca con la estrategia que ya conoces — descartando mitades:
La búsqueda binaria sobre ese índice encuentra 1 valor entre millones en ~20 saltos. Esa es toda la magia: O(log n) en lugar de O(n).
B-tree: búsqueda binaria adaptada a disco
Los índices reales usan árboles B (B-trees): como un árbol binario de búsqueda, pero cada nodo agrupa cientos de claves — porque leer de disco va por bloques, y conviene aprovechar cada lectura al máximo. Resultado: un árbol de 3–4 niveles cubre miles de millones de filas.
Buscar 45: raíz (>40 → derecha) → nodo intermedio → hoja. Tres lecturas en lugar de millones.
Como las hojas están ordenadas y enlazadas, el mismo índice también acelera rangos (WHERE fecha >= ...), ORDER BY y LIMIT — no solo igualdades.
Crear el índice correcto
-- La consulta lenta:
SELECT * FROM usuarios WHERE email = 'ana@ejemplo.com';
-- El arreglo (y garantiza unicidad de paso):
CREATE UNIQUE INDEX idx_usuarios_email ON usuarios (email);
Con varias columnas, el orden dentro del índice importa — es una guía telefónica ordenada por apellido y luego nombre:
CREATE INDEX idx_pedidos_cliente_fecha ON pedidos (cliente_id, fecha);
-- ✅ usa el índice: filtra por la primera columna (y la segunda)
SELECT * FROM pedidos WHERE cliente_id = 42 AND fecha > '2026-01-01';
-- ✅ usa el índice: prefijo del índice
SELECT * FROM pedidos WHERE cliente_id = 42;
-- ❌ NO lo aprovecha: busca "todos los García" sin saber el apellido... al revés
SELECT * FROM pedidos WHERE fecha > '2026-01-01';
Regla del prefijo izquierdo: un índice (a, b) sirve para consultas por a o por a y b, pero no por b sola.
Cuando el índice existe y no se usa
Estas consultas ignoran un índice sobre email o fecha aunque lo tengas:
-- ❌ Función sobre la columna: el índice guarda email, no LOWER(email)
WHERE LOWER(email) = 'ana@ejemplo.com'
-- ❌ Comodín al principio: no hay prefijo por el que navegar el árbol
WHERE email LIKE '%@gmail.com'
-- ❌ Aritmética sobre la columna
WHERE precio * 1.21 > 100
Las soluciones: índice funcional (CREATE INDEX ... ON usuarios (LOWER(email))), reescribir la condición (precio > 100 / 1.21), o para búsqueda de texto, un índice de texto completo.
El precio: las escrituras
Un índice no es gratis. Cada INSERT, UPDATE o DELETE debe actualizar todos los índices de la tabla, y cada índice ocupa disco (a veces tanto como la tabla). Por eso:
- Indexa lo que tus consultas realmente filtran, unen u ordenan — las columnas de tus
WHERE,JOINyORDER BYfrecuentes. - No indexes “por si acaso”: una tabla con 12 índices escribe 13 veces por cada fila.
- Revisa los índices sin uso (las bases de datos llevan estadísticas) y bórralos.
La regla mental: los índices convierten lecturas O(n) en O(log n) a cambio de escrituras algo más lentas. En la mayoría de aplicaciones — que leen mucho más de lo que escriben — es el mejor trato de la casa.