...

MySQL EXPLAIN ANALYZE : bien interpréter les requêtes pour optimiser les performances

Avec « mysql explain », j'analyse la manière dont MySQL 8 élabore un plan effectue et quelles étapes prennent un temps mesurable. C'est ainsi qu'en me basant sur les durées d'exécution réelles, le nombre de lignes et les boucles, je peux identifier où je dois adapter mon plan et Performance d'augmenter de manière ciblée le nombre de mes requêtes.

Points centraux

Pour que tu ailles droit au but, je vais résumer brièvement les principaux objectifs pédagogiques et définir les Priorités. Chaque ligne du plan raconte une histoire, et je te montre ce qui compte vraiment respectais. Lis ces points, vérifie tes requêtes et mets directement en pratique ces conclusions pour optimiser ton site.

  • Durées réelles: EXPLAIN ANALYZE exécute la requête et mesure les durées à chaque étape.
  • Estimations vs réalité: Des écarts importants indiquent des statistiques erronées ou des indices manquants.
  • Format TREE: Le plan sous forme d'arbre met en évidence les itérateurs, les filtres et les jointures.
  • Points chauds: Un „ time to last row “ long et de nombreuses boucles indiquent les objectifs de réglage.
  • Stratégie indicielle: Des indices adaptés (y compris composés) permettent de réduire considérablement les coûts.

Cette liste te donne une idée claire direction, mais ce n'est qu'en lisant concrètement le plan que tu mettras ces connaissances en pratique de manière fructueuse. Je t'expliquerai juste après comment j'interprète chaque indicateur et quelles sont les prochaines étapes Étapes que j'en déduis.

EXPLAIN vs EXPLAIN ANALYZE : ce que je mesure réellement

Avec la commande EXPLAIN classique, je vois le chemin prévu par l'optimiseur, c'est-à-dire un Projet avec une estimation des coûts et du nombre de lignes. Ce plan révèle l'ordre des tables, les index utilisés et la stratégie de jointure, mais sans véritable Valeurs mesurées. EXPLAIN ANALYZE va plus loin et exécute réellement la requête, en mesurant les temps jusqu’à la première et à la dernière ligne ainsi que les boucles. Cela me permet de repérer immédiatement quel nœud de l’arbre prend le plus de temps et par où commencer. Je remplace ainsi les suppositions par des mesures Données et je prends des décisions d'optimisation éclairées.

Syntaxe et cas d'utilisation typiques

Je lance l'analyse à l'aide d'une simple commande : EXPLAIN ANALYZE SELECT ..., car cela me permet de Durées par nœud. La sortie au format TREE affiche les itérateurs tels que les balayages, les jointures, les triages et les filtres avec leurs valeurs estimées et réelles Lignes. Je m'en sers notamment pour les requêtes récurrentes, les instructions UPDATE/DELETE sur plusieurs tables et les requêtes comportant des clauses ORDER BY ou GROUP BY. En option, cela m'aide à FORMAT=JSON, si je souhaite analyser le modèle de coûts en profondeur, mais pour les réglages au quotidien, l'arborescence suffit généralement. Ceux qui souhaitent approfondir les questions relatives à l'optimiseur trouveront de bonnes pistes dans Détails de l'optimiseur, que j'utilise dans la pratique.

Voici comment je lis le plan TREE

Je considère chaque nœud comme une étape distincte qui produit des données ou filtre. Les scans fournissent des lignes issues de tables ou d'index, les jointures relient des flux, les filtres réduisent le nombre de lignes, et les triings classent ou regroupent les Résultats. Les champs „ rows (actual/estimated) “, „ time to first row “, „ time to last row “ et „ loops “ constituent mes principaux repères. Si le nombre réel de lignes s'écarte fortement de l'estimation, je corrige les statistiques ou les index. Si le „ time to last row “ s'étire de manière excessive, je vérifie les tri tardifs, les jointures volumineuses ou les Filtre.

Comprendre les indicateurs clés : de l'estimation à la réalité

Je résume les indicateurs clés dans un tableau clair afin que tu puisses repérer rapidement les signaux typiques reconnais. Chaque ligne t'indique la signification d'un indicateur, le signal d'alerte que j'observe et la mesure à prendre dans la plupart des cas aide.

Chiffre clé Signification signal d'alarme Approche de mise au point
lignes (estimation/réel) Prévues vs. réelles Lignes Écart important (par exemple, 10 contre 100 000) Actualiser les statistiques, compléter les données manquantes Indices vérifier
temps jusqu'à la première ligne Délai jusqu'à la première Édition Lent, malgré le faible nombre de résultats Vérifier le nœud de départ, filtres précoces renforcent
temps nécessaire pour la dernière ligne Durée totale du Nœuds Nettement plus haut que la „ première rangée “ Tri, stratégie de jointure, flux réduire
boucles Fréquence de la Répétition Un très grand nombre d'itérations Réorganiser les jointures, sous-requêtes transformer

Bien interpréter les opérateurs : analyses, jointures, tri

Je fais attention à quel Itérateur qui fait réellement le travail :

  • Balayage d'index par plage/unique: Idéal pour les conditions WHERE sélectives et les préfixes correspondants ; le „ temps jusqu'à la première ligne “ est faible, le „ temps jusqu'à la dernière ligne “ dépend du nombre de résultats.
  • Balayage de table: Signal d'alerte en cas de tables volumineuses ; je cherche alors des filtres adaptés, des index composites ou une reformulation de la requête.
  • Jointure par boucles imbriquées: Stratégie par défaut ; la présence de nombreuses „ boucles “ indique un pilote inadapté ou l'absence d'index sur la table interne.
  • Jointure par hachage (MySQL 8) : Adapté aux jointures Equi de grande taille et uniformément réparties. Le „ time to first row “ peut être plus long (phase de construction), mais le „ time to last row “ en bénéficie lorsque le flux d'échantillons est important.
  • Trier/Groupe: Clairement visibles dans TREE sous forme de nœuds distincts. Des durées d'exécution élevées indiquent souvent un manque de prise en charge par les index.
  • Filtre: Les filtres tardifs indiquent des occasions manquées pour un « Index Condition Pushdown » ou une sélection plus précoce.

Lorsqu'un nœud de tri est dominé par le critère „ time to last row “, je vérifie si l'ordre souhaité peut être obtenu à l'aide d'un index, par exemple en Couverture- des index dont l'ordre de tri correspond. Si la clause ORDER BY correspond à la définition de l'index (sens, préfixe), l'étape de tri est souvent totalement supprimée.

Méthodologie de mesure : comment comparer de manière équitable

Je ne me contente pas d'une seule mesure. Les effets de mise en cache peuvent fausser les résultats, c'est pourquoi :

  • J'exécute EXPLAIN ANALYZE plusieurs fois et j'évalue la médiane et l'étendue plutôt qu'une valeur unique.
  • Je fais la distinction entre le cache „ froid “ et le cache „ chaud “ : les mesures « chaudes » reflètent ce que les utilisateurs constatent après la première exécution.
  • Je fais varier des paramètres représentatifs afin que le plan ne donne pas seulement un bon résultat pour un exemple trivial.
  • Je consigne le schéma et l'état des données afin de pouvoir retracer les résultats ultérieurement.

Pour les instructions DML (UPDATE/DELETE), j'utilise une transaction : START TRANSACTION ; EXPLAIN ANALYZE UPDATE ... ; ROLLBACK ;. Cela me permet d'obtenir des valeurs réelles sans modifications permanentes. Important : EXPLAIN ANALYZE conduit – C'est pourquoi je l'utilise avec prudence sur les systèmes de production.

Statistiques et répartition des données : remédier aux erreurs d'estimation

Les écarts importants entre les lignes „ estimated “ et „ actual “ sont souvent dus à des répartitions asymétriques des données. Je procède alors de deux manières :

  • Mettre à jour les statistiques: Je veille à ce que l'optimiseur dispose d'informations à jour. Des statistiques récentes améliorent le choix des jointures et des index.
  • Utiliser les histogrammes: Dans le cas de colonnes présentant une forte asymétrie, les histogrammes permettent d'estimer les sélectivités de manière plus réaliste. Dans EXPLAIN ANALYZE, l'écart entre l'estimation et la réalité se réduit alors sensiblement.

Si, après l'actualisation, les estimations restent erronées, j'examine les indices composés dans l'ordre des prédicats les plus sélectifs et j'étudie les corrélations entre les colonnes. L'objectif est de faire en sorte que, dès que possible, un nombre réduit de lignes bien pré-filtrées soient transmises aux opérateurs coûteux.

Stratégies de semi-jointure et sous-requêtes

MySQL 8 convertit souvent les prédicats IN/EXISTS en plans de semi-jointures. Dans l'arborescence (TREE), je vois cela sous la forme d'une « Materialization », d'un « FirstMatch » ou d'un « Loose Index Scan ». Je prête attention à :

  • Matérialisation: Un sous-ensemble est créé une seule fois et réutilisé plusieurs fois – ce qui convient bien aux tailles modérées.
  • FirstMatch: S'arrête dès le premier résultat – permet d'économiser des boucles lorsqu'on s'attend à peu de résultats par ligne extérieure.
  • Analyse par index libre: Très efficace pour les modèles de type DISTINCT utilisant des index.

Les sous-requêtes qui s'exécutent pour chaque ligne de la table externe alourdissent les „ boucles “. Je les transforme en JOIN ou je les matérialise délibérément (CTE/Derived) afin que le plan effectue le calcul coûteux une seule fois, puis se contente d'une référence moins gourmande en ressources.

Optimisation ciblée des requêtes SQL : étape par étape

Je commence par la stratégie d'indexation et je sécurise les conditions WHERE et JOIN fréquentes à l'aide de Indices . Si j'ai besoin de plusieurs colonnes pour le filtrage ou le tri, je définis des index composés et j'organise l'ordre des colonnes en fonction des plus fréquentes prédicats. Ensuite, j’optimise les sous-requêtes qui s’exécutent en boucle en les reformulant ou en les transformant en jointures. Je remplace SELECT * par des colonnes spécifiques afin de réduire le volume de données traitées et d’alléger la charge du plan d’exécution. Ensuite, je veille à ce que les statistiques soient à jour, car des estimations imprécises orientent l’optimiseur vers Les chemins de traverse.

Pratique des index : couverture, ordre, expériences

J'utilise trois leviers simples qui apparaissent immédiatement dans EXPLAIN ANALYZE :

  • Indices de couverture: Si l'index contient toutes les colonnes nécessaires (filtre, jointure, projection), le plan évite les recherches dans les tables. Le „ time to last row “ diminue souvent considérablement.
  • Ordre des colonnes: Je trie par sélectivité et par type d'utilisation (filtre avant tri). Pour les clauses ORDER BY et GROUP BY, j'utilise le sens de tri approprié et le préfixe correspondant.
  • Expériences sur les indices: Avec des mesures temporaires, invisibles Pour les index, je vérifie si l'optimiseur les choisirait sans perturber les plans existants. Si le plan s'en trouve amélioré, j'active l'index de manière permanente.

S'il existe plusieurs indices candidats, je compare les plans à l'aide de la commande EXPLAIN ANALYZE et je mesure systématiquement le „ temps jusqu'à la dernière ligne “. En cas de doute, je retiens le plan dont la durée d'exécution est la plus stable pour différentes valeurs de paramètres.

Exemple pratique : lire le plan, établir un index, mesurer les résultats

Je vais répondre à une question fréquente : EXPLAIN ANALYZE SELECT o.id, o.date, c.name FROM orders o JOIN customers c ON c.id = o.customer_id WHERE o.date >= '2025-01-01' ORDER BY o.date DESC; et commence par vérifier le nœud correspondant à la table commandes. Si le plan indique un nombre élevé de lignes réelles et un balayage complet de la table, je crée un index adapté, par exemple sur commandes(date, id_client). Ensuite, je compare le „ time to last row “ avant et après la modification, car ce chiffre illustre très clairement l'effet global montre. Si la clause ORDER BY correspond à l'ordre de l'index, je m'épargne un tri et je réduis considérablement la durée totale. Je mesure ainsi les progrès à l'aide de valeurs concrètes plutôt qu'à l'aide de vagues Impressions.

Analyser les instructions DML en toute sécurité

Pour les instructions UPDATE/DELETE qui modifient le jeu de données, j'adopte une approche structurée :

  • J'encapsule la mesure dans une transaction et je laannule si je souhaite uniquement effectuer la mesure.
  • Je vérifie si les déclencheurs/contraintes entraînent des coûts supplémentaires : la commande EXPLAIN ANALYZE indique des temps d'exécution plus longs dans les nœuds concernés.
  • Je surveille le rapport entre les „ lignes affectées “ et les „ lignes réelles “ : un mauvais rapport indique un filtrage trop tardif ou des index manquants.

Dans le cas des requêtes UPDATE portant sur plusieurs tables, l'ordre des jointures et la couverture des index sont déterminants. Un „ time to last row “ élevé au niveau des nœuds de tri/jointure indique qu'il est possible d'améliorer les index ou de reformuler la requête en deux instructions ciblées avec mise en cache.

Impact de l'hébergement sur les performances des requêtes

Je ne considère pas la base de données isolément, car la mémoire, les E/S et le processeur influencent chaque Durée de validité. Les SSD rapides réduisent le temps d'attente lors de la lecture, une mémoire vive suffisante augmente la taille du pool de tampons, et une pile CPU robuste accélère les opérations de tri, d'agrégation et Joins. Dans les environnements de production, je privilégie les configurations d'hébergement capables de bien prendre en charge les charges de travail gourmandes en données. Je trouve également des informations utiles sur les thèmes liés à l'optimiseur dans Optimiseur interne, que j'utilise comme perspective complémentaire. En associant un plan bien ficelé à un environnement solide, j'obtiens des gains tangibles en matière de Temps de réponse.

Ressources et opérateurs dans leur contexte

Lorsque je consulte le plan, je prête particulièrement attention aux nœuds gourmands en mémoire. Les tri de grande envergure ou les jointures par hachage nécessitent de la mémoire ; s’ils sont trop volumineux, ils sont transférés vers des tables temporaires. Dans l’arborescence (TREE), je le repère grâce à des nœuds tardifs et lents, ainsi qu’à une différence notable entre le „ temps jusqu’à la première ligne “ et le „ temps jusqu’à la dernière ligne “. Je réagis alors comme suit :

  • Réduire le volume des données d'entrée (filtres plus précoces, meilleurs pilotes de jointure).
  • Prise en charge améliorée des index pour l'ordre souhaité, afin d'éviter les doublons.
  • Vérifier si le type de jointure (boucle imbriquée ou hachage) est adapté au volume de données.

Surtout lors des exécutions de rapports, j'exécute EXPLAIN ANALYZE sur des données représentatives, et non sur des mini-instantanés. Ce n'est qu'ainsi que les mesures reflètent les charges réelles.

Bonnes pratiques au quotidien

Je commence par analyser les requêtes qui ressortent dans les journaux ou que les utilisateurs signalent régulièrement comme étant lentes signaler. Ensuite, j'effectue des mesures à l'aide de la commande EXPLAIN ANALYZE, je consigne les chiffres clés et je compare les estimations aux résultats réels. Sur cette base, je modifie de manière ciblée les index et les formulations, et je note les résultats avant et après afin de pouvoir suivre les progrès de manière transparente font. J’intègre ces analyses dès le début du processus de développement, plutôt que d’attendre que des problèmes de production apparaissent. Grâce à des revues régulières, j’identifie plus rapidement les tendances et je prends des décisions plus sûres concernant Tuning-mesures.

Une liste de contrôle pragmatique pour accélérer l'élaboration des plans

  • Votes estimés et réels lignes correspondent-ils globalement ? Si ce n'est pas le cas : vérifier les statistiques/histogrammes.
  • Un nœud domine-t-il le „ time to last row “ ? Premier candidat à l'optimisation (index, choix de jointure, évitement du tri).
  • Les „ boucles “ sont-elles très nombreuses ? Optimisez le pilote de jointure/l'index sur la table interne ou utilisez une semi-jointure.
  • Y a-t-il des tris/regroupements tardifs ? Aligner l'ordre et le sens de l'index sur ORDER BY/GROUP BY.
  • La requête a-t-elle vraiment besoin de toutes ces colonnes ? Privilégier la création d'un index couvrant et rationaliser la liste SELECT.
  • Une sous-requête par ligne ? La transformer en JOIN ou la matérialiser.
  • Stable par rapport aux paramètres ? Effectuer des mesures avec plusieurs valeurs réalistes.

Les erreurs d'interprétation les plus fréquentes et comment les éviter

Je ne me fie pas aveuglément aux estimations Coûts, si le nombre réel de lignes diffère sensiblement. De même, je ne tire aucune conclusion hâtive du „ time to first row “ lorsque le „ time to last row “ représente la majeure partie de la charge porte. Un démarrage rapide ne sert pas à grand-chose si le tri ou la jointure finissent par prendre le dessus. De plus, j'examine minutieusement les boucles, car elles cachent souvent une jointure inefficace ou une sous-requête qui s'exécute ligne par ligne. Ce n'est que lorsque le plan, les mesures et la répartition des données concordent que je modifie choses.

Cas particuliers : CTE, tables dérivées, partitions

Les expressions de table communes (CTE) et les tables dérivées peuvent être matérialisées ou fusionnées. Dans TREE, je considère la matérialisation comme une étape distincte de la construction. C'est utile lorsque le sous-flux est utilisé plusieurs fois ou que son calcul est coûteux. Si les CTE ne sont utilisées qu’une seule fois et qu’elles sont sélectives, une fusion est souvent plus avantageuse, car elle évite un travail de stockage supplémentaire. Je vérifie si le „ temps jusqu’à la première ligne “ augmente fortement : dans ce cas, la matérialisation est peut-être surdimensionnée.

Les tables partitionnées sont utiles pour les grands volumes de données lorsque le prédicat délimite clairement les partitions. Je vérifie dans le plan si l'élagage s'applique (seules quelques partitions sont analysées). S'il n'y en a pas, les coûts se répartissent sur toutes les partitions – ce qui indique qu'il faut adapter les clés de partitionnement aux filtres les plus fréquents ou reformuler la requête de manière à permettre l'élagage.

En bref

Grâce à EXPLAIN ANALYZE, je peux évaluer les plans d'exécution MySQL et mettre en évidence les points sensibles, que je traite ensuite avec Indices, la reformulation des requêtes et les statistiques actuelles. Je me concentre sur les écarts entre le nombre de lignes estimé et le nombre réel, les temps nécessaires pour atteindre la première et la dernière ligne, ainsi que les boucles. J'en déduis quelques étapes efficaces et je vérifie à nouveau chaque effet à l'aide de la commande EXPLAIN ANALYZE. Avec le temps, je reconnais immédiatement les schémas récurrents et je mets en œuvre plus rapidement les mesures appropriées. C'est ainsi que j'améliore la Performance fiable et assure la stabilité des requêtes à long terme.

Derniers articles

Centre de données avec des baies de serveurs et une visualisation stylisée des données pour l'optimisation des performances de MySQL
Bases de données

Histogrammes MySQL – De meilleurs plans de requête sans index

Découvrez comment les histogrammes MySQL fournissent à l'optimiseur des statistiques précises, permettent d'élaborer de meilleurs plans de requête et améliorent considérablement l'optimisation de vos requêtes SQL sans avoir recours à des index supplémentaires.

Rack de serveurs avec hébergement CloudLinux et PHP Selector dans un centre de données moderne
Serveurs et machines virtuelles

CloudLinux PHP Selector – Fonctionnement et limites en pratique

Tout savoir sur CloudLinux PHP Selector : comment gérer votre version de PHP, activer des extensions et ajuster les limites en toute sécurité dans votre hébergement – idéal pour les environnements d'hébergement mutualisé modernes.