11 points par newcodes7 2026-01-19 | Aucun commentaire pour le moment. | Partager sur WhatsApp

Présentation du projet

  • NewCodes est un service de curation de blogs techniques d’entreprise
  • Architecture Spring Boot + PostgreSQL
  • Mise en œuvre d’une fonctionnalité d’autocomplétion : recommandations basées sur les Term, recherche avec décomposition des jamo, recherche par consonnes initiales, recommandation de pages d’entreprise

Découverte du problème de performance

  • 110 000 données accumulées dans la table Term
  • Le temps de réponse de l’API a dépassé 1000 ms
  • Objectif : répondre en moins de 100 ms

1re tentative : ajout d’index (1000 ms → 700 ms)

  • Création d’index optimisés pour les recherches par préfixe avec LIKE en utilisant varchar_pattern_ops
  • Création des index avec l’option CONCURRENTLY, sans interruption de service
  • Application d’un index sur chacune des colonnes term, decomposed_term et chosung

2e tentative : index sur la fonction LOWER (700 ms → 110 ms)

  • Identification d’un problème de scan complet provoqué par l’utilisation de la fonction LOWER()
  • Création d’un index fonctionnel (Functional Index)
  • Refonte des index sous la forme LOWER(nom_de_colonne) varchar_pattern_ops

3e tentative : JOIN → EXISTS (110 ms → 100 ms)

  • Le INNER JOIN entre Corporation et Article était le principal goulot d’étranglement
  • Remplacement par une sous-requête EXISTS pour réduire la zone de scan
  • Optimisation pour ne vérifier que la « présence de données »

4e tentative : dénormalisation & index de couverture (100 ms → 90 ms)

  • Ajout de la colonne total_frequency pour supprimer les opérations d’agrégation
  • Remplacement des opérations GROUP BY et SUM par des valeurs pré-calculées
  • Réduction des opérations d’I/O grâce à un index de couverture
  • Inclusion de term et total_frequency dans l’index avec la clause INCLUDE

5e tentative : JDBC Template (90 ms → 80 ms)

  • Suppression de la surcharge de JPA/Hibernate
  • Exécution directe des requêtes avec JDBC Template
  • Pour les lectures simples, ignorer la couche ORM s’est révélé efficace

Résolution du problème de Rate Limiting Nginx

  • Configuration initiale : limite de 2 requêtes par seconde, burst 10
  • Des échecs de requêtes sont apparus à cause d’un debouncing à 100 ms
  • Amélioration : passage à 10 requêtes par seconde, burst 20
  • Changement du code de statut de 444 à 429

Réduction de la taille des données de réponse

  • Suppression des noms de champs JSON et passage à une réponse basée sur des tableaux
  • Distinction des types par des numéros (0: Corporation, 1: Theme, 2: Term)
  • Réduction du temps de transfert réseau

Traitement parallèle avec CompletableFuture

  • Exécution simultanée et indépendante des requêtes Corporation, Theme et Term
  • Le temps total ne correspond plus qu’au temps de réponse maximal, au lieu d’une exécution séquentielle
  • Ajout d’ExecutorService et de la gestion des exceptions

Résultat final

  • 1000 ms au départ → 80 ms au final (serveur de développement), 40 ms (serveur de production)
  • Amélioration des performances de plus de 90 %

Principaux enseignements

  • Importance de bien définir le problème et la direction à prendre
  • Trouver le bon équilibre entre utilisation de l’IA et validation par le développeur
  • Nécessité d’une conception à l’échelle de toute l’architecture
  • Choix du type d’index : index simple / composite / de couverture
  • Attention à l’invalidation des index lors de l’utilisation de fonctions
  • Compréhension du fonctionnement interne de JPA
  • Analyse des plans d’exécution des requêtes avec EXPLAIN

Pistes d’amélioration futures

  • Utilisation d’une structure de données Trie
  • Mise en cache des termes les plus recherchés
  • Utilisation d’un CDN (dans le cas d’un service mondial)

Aucun commentaire pour le moment.

Aucun commentaire pour le moment.