Parte IV · Pulir y proteger
Capítulo 14 — ¿Y la velocidad? (índices, idea intuitiva)
Al acabar este capítulo sabrás por qué una consulta que funcionaba al instante puede volverse lentísima cuando la base de datos crece, qué es un índice y cómo usarlo para que tu juego responda rápido aunque tenga millones de filas. También aprenderás algo que se repite en este libro: casi todo en bases de datos es un compromiso, y elegir bien es cosa tuya.
En el capítulo 13 pusiste a salvo tus datos: un intercambio de cartas ya no deja a nadie a medias aunque se vaya la luz. Si haces recuento, tu base de datos de Kriaturas ya sabe hacer de casi todo. Guarda jugadores, cartas, mazos, partidas y amistades. Está bien diseñada (capítulos 6 a 10). Está protegida con reglas que se cumplen solas (capítulo 11). Sabe cruzar tablas para sacar rankings y estadísticas (capítulo 12). Y no se rompe a la mitad de una operación (capítulo 13).
Queda una última pregunta, y es de las que solo aparecen cuando las cosas van bien. El día que Kriaturas tenga éxito de verdad —diez millones de partidas jugadas, cientos de miles de cartas en circulación—, ¿seguirá respondiendo rápido? Porque mostrar el ranking, o buscar una carta por su nombre, podría pasar de tardar un parpadeo a tardar una eternidad.
¿Cómo se las apañan los juegos de verdad para responder al instante con tantísima información detrás? La respuesta cabe en una palabra: índices.
Buscar a lo bruto
Volvamos a algo que ya sabes hacer desde el capítulo 3. Quieres encontrar la carta Chispín por su nombre:
SELECT * FROM carta
WHERE nombre = 'Chispín';
Con las seis cartas de Kriaturas, esta consulta es instantánea. Pero imagina por
un momento que tu juego ha crecido y la tabla carta tiene un millón de
filas. ¿Qué hace la base de datos para responder?
Sin ninguna ayuda, hace lo único que puede hacer: recorrer las filas una a una. Mira la fila 1, ¿se llama Chispín? No. Fila 2, ¿se llama Chispín? No. Fila 3… y así hasta encontrarla. En el peor caso, si Chispín está al final (o si no existe), tiene que mirar el millón de filas antes de poder responderte.
A esa forma de buscar se le llama recorrido secuencial: leer la tabla entera, de arriba abajo, comparando cada fila. Es como buscar una palabra en un libro leyéndolo página por página desde la primera. Con un libro de diez páginas, da igual. Con uno de mil, te pasas la tarde.
La idea importante es esta: buscar a lo bruto no escala. Con pocos datos no se nota. Cuando la tabla se hace enorme, cada búsqueda se vuelve cara, y si tu juego hace miles de búsquedas por segundo, el sistema se ahoga. Hace falta una forma de ir directo al dato, sin leerlo todo.
El truco del índice del libro
Resulta que ese problema ya está resuelto, y la solución la tienes en las manos ahora mismo. Coge un libro de texto cualquiera —uno de papel, de los gordos— y ve al final. Ahí hay un índice: una lista ordenada alfabéticamente de términos, y al lado de cada uno, la página donde aparece.
Si quieres encontrar dónde habla de "fotosíntesis", no lees el libro entero. Vas al índice, que está ordenado, saltas directo a la efe, encuentras "fotosíntesis → página 214" y vas a esa página. Dos pasos en vez de mil. Eso es exactamente lo que hace un índice en una base de datos.
Un índice es una estructura auxiliar, ordenada, que la base de datos mantiene aparte de la tabla. Asocia el valor de una columna con la posición de las filas que lo contienen. Gracias a que está ordenado, la base de datos puede ir directa al valor que busca sin recorrer toda la tabla.
Visto por encima, un índice sobre el nombre de las cartas sería algo así:
nombre → fila ... Burbujo → fila 3 Chispín → fila 1 Flamita → fila 2 Hojarasca → fila 4 ...
La diferencia es la misma que entre el índice del libro y leer el libro: con la lista ordenada, encontrar "Chispín" es cuestión de un salto, no de un millón de comparaciones. A esto se le llama acceso por valor: vas directo a partir del valor que buscas, en lugar de pasar por todas las filas.
Para sacar la duda de en medio. Esa lista ordenada no es una copia de la tabla, y no la mantienes tú a mano. La crea y la actualiza la propia base de datos por su cuenta. Tú solo le dices "quiero un índice sobre esta columna" una vez, y a partir de ahí ella se encarga del resto. Por dentro no es literalmente una lista, sino una estructura más ingeniosa pensada para que el salto sea rapidísimo, pero la idea es justo la del índice del libro. Cómo es esa estructura por dentro lo dejamos para "para saber más".
Cómo se crea un índice
La orden es de las más cortas de todo el libro. Para que la búsqueda de cartas
por nombre deje de ser un recorrido secuencial, creas un índice sobre la columna
nombre:
CREATE INDEX idx_carta_nombre ON carta(nombre);
Léelo despacio: "crea un índice, que se llame idx_carta_nombre, sobre la
columna nombre de la tabla carta". El nombre del índice te lo inventas tú; la
costumbre es ponerle algo que recuerde qué indexa (idx_ + tabla + columna), para
reconocerlo de un vistazo más adelante.
Y ya está. No tienes que cambiar tus consultas. La misma búsqueda de antes…
SELECT * FROM carta
WHERE nombre = 'Chispín';
…ahora usa el índice por debajo, sin que tú hagas nada distinto. La base de datos
se da cuenta sola de que existe un índice sobre nombre y lo aprovecha. Tu
consulta devuelve exactamente el mismo resultado que antes; lo único que
cambia es que llega muchísimo más rápido.
Subraya esa idea, porque es la que más se confunde: un índice no cambia los datos ni cambia el resultado de una consulta. Solo cambia la velocidad. Con índice o sin él, Chispín es Chispín. La diferencia es si la base de datos tarda un parpadeo o tarda un buen rato en dártelo.
Lo mismo vale para el ranking del capítulo 12. Aquel ranking agrupaba la tabla
partida por la columna ganador para contar victorias. Cuando partida tenga
millones de filas, ese recuento se vuelve pesado. Un índice sobre la columna por
la que agrupas y filtras ayuda a la base de datos a hacerlo mucho más rápido:
CREATE INDEX idx_partida_ganador ON partida(ganador);
Mismo ranking, misma consulta, mismos números. Solo que ahora sale al instante aunque haya diez millones de partidas detrás.
Lo que cuesta un índice
Si los índices son tan maravillosos, surge la pregunta obvia: ¿por qué no poner un índice en cada columna de cada tabla y acabar antes? Sería como tener un ranking instantáneo para todo.
Aquí aparece el "pero", y es importante. Los índices no son gratis. Tienen dos costes que conviene tener claros.
El primero es espacio. Un índice es una estructura más que la base de datos guarda aparte. Ocupa sitio. Cuantos más índices tengas, más espacio extra consume tu base de datos, encima del que ya ocupan las tablas.
El segundo, y más importante, es el coste de mantenimiento. Acuérdate de que el índice tiene que estar siempre ordenado y al día. ¿Qué pasa cuando insertas una carta nueva, o cambias el nombre de una, o borras una? Que la base de datos no solo tiene que tocar la tabla: también tiene que actualizar todos los índices de esa tabla para que sigan correctos. Cada índice extra hace que insertar, modificar y borrar sea un poquito más lento.
Ahí está el compromiso, y es la idea central del capítulo:
Un índice acelera las lecturas (las búsquedas) pero ralentiza un poco las escrituras (insertar, modificar, borrar) y ocupa espacio. Indexas cuando lo que ganas leyendo compensa lo que pagas escribiendo.
Por eso no se indexa todo. Se indexa lo que se consulta mucho. La regla práctica es sencilla:
- Sí merece la pena indexar las columnas por las que buscas o filtras a
menudo (el
nombreo lararezade las cartas, si tu juego deja buscar por ahí), las columnas por las que ordenas con frecuencia, y las claves foráneas que usas para cruzar tablas en losJOIN(comopartida.ganadoromazo.jugador_id). - No merece la pena indexar columnas por las que casi nunca buscas, ni tablas pequeñas (con pocas filas, el recorrido secuencial ya es instantáneo: el índice solo añadiría coste sin aportar velocidad).
Si esto te suena, es porque ya lo viviste antes con otro nombre. En el capítulo 8 te encontraste el dilema "¿guardar o calcular?" con el rango del jugador. En el capítulo 10, la desnormalización repetía un dato a propósito para ganar velocidad. Los índices son la misma historia contada otra vez: gastas algo (espacio, escrituras más lentas) para ganar otra cosa (lecturas rapidísimas). En bases de datos casi nunca hay magia gratis; hay compromisos bien elegidos. Y elegir bien, sabiendo qué ganas y qué pagas, es justo lo que distingue a quien entiende lo que hace.
Resumen
Cuando una tabla crece hasta tener millones de filas, buscar un dato recorriendo todas las filas una a una (el recorrido secuencial) se vuelve lento. Buscar a lo bruto no escala.
Un índice resuelve eso. Funciona como el índice de un libro: una lista
ordenada que te lleva directo al dato sin leerlo todo (acceso por valor). Lo
creas con una orden tan corta como CREATE INDEX idx_carta_nombre ON
carta(nombre); y, a partir de ahí, tus consultas de siempre van mucho más
rápido sin que cambies nada. Un índice cambia la velocidad, nunca el
resultado.
Pero no es gratis: ocupa espacio y hace un poco más lentas las escrituras, porque
hay que mantenerlo al día. Por eso no se indexa todo, solo lo que se consulta
mucho: columnas de búsqueda, columnas de orden y claves foráneas de los JOIN.
Es el mismo compromiso que ya viste con el "¿guardar o calcular?" del
capítulo 8 y la desnormalización del capítulo 10: gastas algo para ganar otra
cosa, y elegir bien es cosa tuya.
Con esto cierras la caja de herramientas. Ya sabes diseñar una base de datos relacional, protegerla y acelerarla. Pero las tablas no son la única forma de guardar datos en el mundo. ¿Qué hay más allá del modelo relacional? ¿Y dónde encaja de verdad la IA en toda esta historia? De eso va el último capítulo.