En la mayoría de aplicaciones web, el código de la API pasa buena parte del tiempo esperando a la base de datos. Es el sitio donde una mala decisión se nota más: una consulta sin índice sobre una tabla grande puede costar miles de veces más que la misma consulta bien planteada. Esta unidad repasa las tres cosas que controlas: el esquema, los índices y las consultas.
1 // El trabajo invisible de una consulta
Cuando escribes SELECT y recibes filas, el servidor de base de datos ha tenido que analizar la consulta, decidir un plan de ejecución, leer páginas de datos del disco o de la memoria, filtrar, ordenar y enviar el resultado. Todo eso es CPU, memoria y operaciones de disco.
Además, las bases de datos son servicios que se dimensionan por su pico: si unas consultas ineficientes obligan a tener un servidor con el doble de memoria y CPU, ese servidor más grande estará encendido todo el día, no sólo en el momento de la consulta lenta. Por eso la eficiencia en la base de datos ahorra doblemente: energía por consulta y tamaño de la infraestructura.
2 // El esquema: guardar lo justo
El diseño de las tablas decide cuánto ocupa cada fila, y el tamaño de las filas decide cuántas caben en memoria y cuántas hay que leer del disco.
| Decisión | Menos eficiente | Más eficiente |
|---|---|---|
| Un estado con pocos valores | VARCHAR(255) con texto repetido |
TINYINT o ENUM |
| Una fecha | Texto '2026-09-17' |
DATE |
| Un identificador numérico | BIGINT cuando nunca pasará de miles |
INT o menor, según el rango real |
| Datos que casi nunca se leen | En la tabla principal, junto a lo que se consulta siempre | En una tabla aparte |
| Ficheros e imágenes | Guardados como BLOB en la tabla |
En almacenamiento de objetos, con la ruta en la tabla |
La normalización que has estudiado evita duplicar datos y ahorra espacio. A veces se desnormaliza a propósito, copiando un dato para evitar un JOIN costoso que se repite millones de veces. Ninguna de las dos opciones es siempre la buena: la pregunta es qué consulta se ejecuta más y qué diseño la abarata.
Guardar «por si acaso» también tiene huella. Registros que nadie consulta, tablas de auditoría sin límite o históricos de hace diez años ocupan disco, se incluyen en cada copia de seguridad y hacen más lentas las consultas. Define cuánto tiempo necesitas cada dato y borra o archiva lo que sobra.
3 // Índices: el atajo con coste
Un índice es una estructura adicional, normalmente un árbol B, que permite encontrar filas por el valor de una columna sin recorrer la tabla entera. Es como el índice de un libro: en lugar de leer las 800 páginas, vas directo a la que buscas.
Una tabla pedidos tiene 5 millones de filas y la consulta más frecuente busca los pedidos de un cliente concreto: WHERE cliente_id = 1234. Cada cliente tiene unos 20 pedidos.
- Sin índice: el motor recorre las 5.000.000 de filas y se queda con 20.
- Con índice en
cliente_id: el árbol B, con cientos de claves por nodo, tiene 3 o 4 niveles. El motor baja por el árbol y lee unas 25 entradas en total, entre nodos y filas. - Proporción: del orden de 200.000 veces menos lecturas.
Pero los índices no son gratis:
- Cada escritura es más cara: un
INSERTo unUPDATEtiene que actualizar la tabla y todos sus índices. - Ocupan disco y memoria: en tablas muy indexadas, los índices pueden ocupar más que los propios datos.
- Un índice sin uso es puro coste.
La herramienta para decidir es EXPLAIN: antepuesta a una consulta en MySQL, muestra el plan de ejecución, si se usa algún índice y cuántas filas estima leer. Si ves que recorre la tabla completa en una consulta frecuente, ahí hay trabajo.
4 // Consultas que no leen de más
Con el esquema y los índices bien, queda la forma de preguntar. Los errores más comunes:
SELECT *: trae todas las columnas aunque uses dos. Más lectura, más memoria, más datos por la red. Pide sólo las columnas que necesitas.- Las N+1 consultas: obtener 100 pedidos y lanzar después una consulta por cada uno para buscar su cliente. Son 101 viajes a la base de datos donde bastaba uno con
JOIN. Los ORM lo provocan con facilidad si no se configura la carga de relaciones. - Filtrar en el código lo que podía filtrar la base de datos: traer 10.000 filas para quedarte con 10 en JavaScript o en PHP.
- Funciones sobre columnas indexadas:
WHERE YEAR(fecha) = 2026impide usar el índice defecha;WHERE fecha >= '2026-01-01' AND fecha < '2027-01-01'sí lo aprovecha. - Paginar con
OFFSETenorme:LIMIT 20 OFFSET 100000obliga a leer y descartar 100.000 filas. Paginar por clave («los siguientes a este id») evita ese trabajo.
El temario menciona también Elasticsearch, muy usado para búsquedas de texto. Los principios son los mismos con otro vocabulario: definir el mapping a mano en lugar de dejar que lo adivine, no indexar campos por los que nunca se busca, pedir sólo los campos necesarios en _source y no crear muchos más shards de los que el volumen de datos justifica, porque cada uno consume memoria aunque esté casi vacío.
Una base de datos eficiente guarda lo justo, indexa lo que se consulta y pregunta sólo por lo que necesita. Usa EXPLAIN antes de dar por buena una consulta frecuente: es la forma más barata de ahorrar servidores.
Autoevaluación
¿Por qué un índice reduce el consumo de una consulta?
Si los índices aceleran las consultas, ¿por qué no indexar todas las columnas?
¿Qué es el problema de las N+1 consultas y por qué desperdicia recursos?
IN, obtiene lo mismo con una fracción del trabajo.