Cómo una columna sin índice mató nuestra base de datos

Un solo índice faltante convirtió una API de 8 ms en una pesadilla de 8 segundos.

Realizamos una prueba de carga antes de un lanzamiento importante. El desarrollo local manejaba 100 filas sin problemas. En staging simulamos 500 usuarios contra 1,5 millones de filas.

Todo falló.

  • Los tiempos de respuesta de la API saltaron de 45 ms a 8.000 ms.
  • El uso de CPU de la base de datos alcanzó el 100 %.
  • Los pools de conexiones se agotaron.
  • Las solicitudes agotaron el tiempo de espera.

El culpable fue una consulta simple que obtenía el historial de pedidos por user_id y status.

Al ejecutar EXPLAIN ANALYZE en PostgreSQL, se mostró un Sequential Scan. Al no haber un índice en user_id, el motor leyó los 1,5 millones de filas en cada solicitud.

Con 100 solicitudes concurrentes, la base de datos escaneó 150 millones de filas a la vez.

La solución tomó cinco minutos. Creamos un índice compuesto en (user_id, status, created_at DESC) usando CREATE INDEX CONCURRENTLY para que la tabla permaneciera en línea.

Resultados:

  • El tiempo de la consulta bajó de 8.150 ms a 0,14 ms.
  • La latencia de la API cayó de 8 segundos a 12 ms.
  • El uso de CPU bajó del 100 % a menos del 8 %.

Lecciones aprendidas:

  • Las pruebas locales engañan; 100 filas no son un sustituto de un millón.
  • Indexa las claves foráneas; la mayoría de los ORM omiten esto.
  • Ejecuta EXPLAIN ANALYZE; la base de datos señala el problema.
  • Optimiza las consultas antes de comprar más hardware.

No escales los servidores primero. Escala las consultas.

Fuente: https://dev.to/mia_keller_ffd2584c046ecb/how-an-unindexed-column-silently-killed-our-database-under-load-and-the-5-minute-fix-m32