Article technique
Analyser une requête MySQL lente avec EXPLAIN
Utilisez un exemple reproductible pour lire un plan MySQL, étudier un index proposé et comparer les résultats.
- SQL
- MySQL

Dans cet article
Consigner la requête et son contexte
L’analyse d’une requête MySQL lente ne commence pas par l’ajout d’un index. Conservez le SELECT exact, ses paramètres, la version, les index présents, le volume et les circonstances du ralentissement. Distinguez les attentes de verrous du travail d’exécution et des entrées-sorties. L’exemple utilise huit commandes fictives et la même requête avant et après un index candidat. C’est une démonstration reproductible, pas un benchmark de production ni un gain mesuré. Consultez la documentation de votre version lorsque la présentation diffère et conservez le plan initial.
Exécuter uniquement dans un environnement de test
Ouvrez une nouvelle connexion à une base de test MySQL 8.4 distincte. Exécutez le bloc entier dans cette session et arrêtez-vous à la première erreur. Seule une table temporaire propre à la session est créée. Ne l’utilisez pas en production et ne réutilisez pas une session ayant une table du même nom. Fermez la connexion pour supprimer la table temporaire. EXPLAIN FORMAT=TRADITIONAL explicite le format attendu ; ce plan MySQL n’est pas un plan SQLite ou SQL Server. Les droits de création de tables temporaires sont nécessaires.
-- Synthetic MySQL 8.4 demo. Use a NEW session in a separate test database.
-- Never run in production. Stop at the first error; close the session afterwards.
CREATE TEMPORARY TABLE cg_explain_orders (
id INTEGER NOT NULL PRIMARY KEY,
customer_id INTEGER NOT NULL,
created_at DATETIME NOT NULL
);
INSERT INTO cg_explain_orders VALUES
(1,42,'2026-08-31 23:59:59'), (2,42,'2026-09-01 00:00:00'),
(3,42,'2026-09-01 12:00:00'), (4,42,'2026-09-01 12:00:00'),
(5,42,'2026-09-02 00:00:00'), (6,7,'2026-09-01 12:00:00'),
(7,42,'2026-09-01 23:59:59'), (8,42,'2026-09-02 00:00:01');
EXPLAIN FORMAT=TRADITIONAL
SELECT id, customer_id, created_at FROM cg_explain_orders
WHERE customer_id = 42
AND created_at >= '2026-09-01 00:00:00'
AND created_at < '2026-09-02 00:00:00'
ORDER BY created_at DESC, id DESC;
SELECT id, customer_id, created_at FROM cg_explain_orders
WHERE customer_id = 42
AND created_at >= '2026-09-01 00:00:00'
AND created_at < '2026-09-02 00:00:00'
ORDER BY created_at DESC, id DESC;
CREATE INDEX cg_explain_customer_date_id
ON cg_explain_orders(customer_id, created_at, id);
EXPLAIN FORMAT=TRADITIONAL
SELECT id, customer_id, created_at FROM cg_explain_orders
WHERE customer_id = 42
AND created_at >= '2026-09-01 00:00:00'
AND created_at < '2026-09-02 00:00:00'
ORDER BY created_at DESC, id DESC;
SELECT id, customer_id, created_at FROM cg_explain_orders
WHERE customer_id = 42
AND created_at >= '2026-09-01 00:00:00'
AND created_at < '2026-09-02 00:00:00'
ORDER BY created_at DESC, id DESC;
-- Both SELECTs should return IDs 7, 4, 3, 2. No timing claim follows from this.Ne pas confondre estimations et mesures
Dans EXPLAIN traditionnel, possible_keys liste des index possibles et key indique l’index retenu. type décrit l’accès ; rows et filtered permettent d’interpréter le travail estimé. Extra ajoute notamment des indications de tri. Voir un nom d’index ne prouve pas l’efficacité de toute la requête. Avec huit lignes, un parcours complet peut être moins coûteux : ne pas choisir l’index candidat n’est donc pas automatiquement une anomalie. Examinez l’hypothèse : la combinaison client, période et tri pourrait-elle réduire le travail sur des données représentatives ?
Vérifier les résultats avant la vitesse
Les deux SELECT doivent retourner les ID 7, 4, 3 et 2. La ligne 2 contrôle la borne de début incluse ; les lignes 5 et 8 sont hors de l’intervalle semi-ouvert. La ligne 6 appartient à un autre client. Les dates égales de 3 et 4 sont ordonnées sans ambiguïté grâce à l’ID. Pour une vraie charge, ajoutez valeurs NULL, résultats vides, grands clients et distribution réaliste. Des nombres égaux ne prouvent pas des contenus égaux. Comparez identifiants, valeurs et ordre en préservant les filtres d’autorisation.
Mesurer la charge réelle de façon contrôlée
EXPLAIN ANALYZE exécute réellement un SELECT pris en charge et fournit des informations d’exécution. C’est utile, mais ce n’est pas une inspection sans risque d’une requête lourde en production. Utilisez un environnement représentatif et notez version, paramètres, cache et charge simultanée. Répétez les mesures au lieu de ne publier que le meilleur essai. Mesurez aussi le coût des écritures et le stockage. Cette fixture contrôle la logique, pas la performance de production. Elle ne justifie aucun gain universel. Documentez les validations restantes avant de proposer un déploiement.
Poursuivre l’analyse
Un plan de requête ne représente qu’une partie du diagnostic. Les références ci-dessous relient l’analyse technique à un service SQL délimité. Les démonstrations utilisent des données fictives et non les résultats d’une mission client. Pour une demande, commencez par décrire le problème avec un contexte anonymisé, sans mot de passe, dump complet ni données personnelles.
Dernière mise à jour :


