Aller au contenu principal

Optimiser les requêtes MySQL dans une application Laravel

Illustration abstraite représentant une base de données MySQL analysée avec une loupe

La majorité des lenteurs constatées sur une application Laravel ne viennent pas du framework, mais de la façon dont les requêtes MySQL sont construites. Le problème N+1, les index manquants et les requêtes exécutées sans nécessité réelle représentent l’essentiel des cas rencontrés. Voici comment les identifier et les corriger.

Sommaire

  1. Le problème N+1 et l’eager loading
  2. Index manquants : comment les repérer
  3. Lire un plan d’exécution avec EXPLAIN
  4. Ne sélectionner que les colonnes nécessaires
  5. Pagination et comptage
  6. Outils de diagnostic dans Laravel

Le problème N+1 et l’eager loading

Le problème N+1 survient quand une boucle sur une collection Eloquent déclenche une requête supplémentaire pour chaque élément — par exemple, afficher l’auteur de chaque article d’une liste sans avoir chargé la relation à l’avance déclenche une requête par article. La solution est l’eager loading via with() : Article::with('auteur')->get() charge tous les auteurs en une seule requête supplémentaire, quel que soit le nombre d’articles. Le paquet barryvdh/laravel-debugbar ou la méthode Model::preventLazyLoading() en développement permettent de détecter ces cas automatiquement.

Index manquants : comment les repérer

Une colonne utilisée dans une clause WHERE, un JOIN ou un ORDER BY sans index associé force MySQL à parcourir la table entière (full table scan) au lieu d’utiliser une structure de recherche rapide. Les clés étrangères devraient systématiquement être indexées, tout comme les colonnes fréquemment filtrées (un statut, une date de publication, un slug). Les migrations Laravel permettent de l’ajouter simplement : $table->index('status') ou $table->foreignId('user_id')->constrained(), qui indexe automatiquement la clé étrangère.

Lire un plan d’exécution avec EXPLAIN

La commande EXPLAIN devant une requête SQL montre comment MySQL prévoit de l’exécuter : quel index est utilisé (ou non), combien de lignes sont examinées, dans quel ordre les tables sont jointes. Une valeur type: ALL dans le résultat signale un scan complet de table — souvent le signe qu’un index manque. Laravel expose cette même information directement sur une requête Eloquent via DB::table('articles')->where('status', 'publie')->explain(), sans avoir à réécrire la requête en SQL brut.

Ne sélectionner que les colonnes nécessaires

Un Model::all() ou Model::get() sans précision récupère toutes les colonnes de la table, y compris des champs volumineux (texte long, JSON) qui ne seront jamais affichés. Préciser les colonnes avec select(['id', 'titre', 'slug']) réduit la quantité de données transférées entre MySQL et l’application, ce qui compte particulièrement sur des tables à fort volume ou des colonnes de grande taille.

Pagination et comptage

La pagination classique de Laravel (paginate()) exécute deux requêtes : une pour les résultats de la page, une pour le total (COUNT(*)), coûteuse sur de très grandes tables. Quand l’affichage du nombre total de résultats n’est pas indispensable (un fil d’actualité, un défilement infini), simplePaginate() ou cursorPaginate() évite ce second comptage et reste performant même sur des tables de plusieurs millions de lignes.

Outils de diagnostic dans Laravel

Au-delà de Laravel Debugbar en développement, Laravel Telescope permet d’observer en détail les requêtes exécutées sur un environnement de test, avec leur durée et leur origine dans le code. En production, l’activation ponctuelle du slow query log de MySQL (long_query_time) remonte les requêtes réellement lentes exécutées par de vrais utilisateurs, souvent différentes de celles identifiées en développement sur un jeu de données réduit.

Chez Any In IT, l’optimisation des requêtes fait partie de notre travail courant sur les applications Laravel à fort trafic, avant d’envisager des solutions plus coûteuses comme la mise en cache ou la réplication de base de données.

À lire aussi

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *