Comment une colonne non indexée a tué notre base de données

Un seul index manquant a transformé une API de 8 ms en un cauchemar de 8 secondes.

Nous avons effectué un test de charge avant une mise en production majeure. En développement local, 100 lignes passaient sans problème. En staging, nous avons simulé 500 utilisateurs sur 1,5 million de lignes.

Tout s'est effondré.

  • Les temps de réponse de l'API sont passés de 45 ms à 8 000 ms.
  • Le CPU de la base de données a atteint 100 %.
  • Les pools de connexions ont été épuisés.
  • Les requêtes ont expiré.

Le coupable était une simple requête qui récupérait l'historique des commandes par user_id et status.

L'exécution de EXPLAIN ANALYZE sur PostgreSQL a révélé un Sequential Scan. Sans index sur user_id, le moteur lisait l'intégralité des 1,5 million de lignes pour chaque requête.

Avec 100 requêtes simultanées, la base de données a scanné 150 millions de lignes d'un coup.

Le correctif a pris cinq minutes. Nous avons créé un index composite sur (user_id, status, created_at DESC) en utilisant CREATE INDEX CONCURRENTLY afin que la table reste accessible.

Résultats :

  • Le temps de requête est passé de 8 150 ms à 0,14 ms.
  • La latence de l'API est passée de 8 secondes à 12 ms.
  • L'utilisation du CPU est descendue de 100 % à moins de 8 %.

Leçons apprises :

  • Les tests locaux sont trompeurs ; 100 lignes ne sont pas représentatives d'un million.
  • Indexez les clés étrangères — la plupart des ORM oublient de le faire.
  • Utilisez EXPLAIN ANALYZE ; la base de données vous indique le problème.
  • Optimisez vos requêtes avant d'acheter du matériel supplémentaire.

Ne commencez pas par augmenter la capacité des serveurs. Optimisez vos requêtes.

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